pkglint

package module
v5.7.21+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 20, 2019 License: BSD-2-Clause Imports: 30 Imported by: 0

README

Build Status codecov

pkglint checks whether a pkgsrc package conforms to the various conventions established over the years. It produces warnings, errors and notes and, upon request, explains them.

Before importing a new package or making changes to an existing package, pkglint should be run in the package's directory to check for common errors.

See https://www.pkgsrc.org/.


For an introduction to programming and extending pkglint, see The pkglint tour.

Documentation

Index

Constants

View Source
const (
	// List is a compound type, consisting of several space-separated elements.
	// Elements can have embedded spaces by enclosing them in double or single
	// quotes, like in the shell.
	//
	// These lists are used in the :M, :S modifiers, in .for loops,
	// and as lists of arbitrary things.
	List vartypeOptions = 1 << iota

	Guessed
	PackageSettable
	UserSettable
	SystemProvided
	CommandLineProvided

	// NeedsRationale marks variables that should always contain a comment
	// describing why they are set. Typical examples are NOT_FOR_* variables.
	NeedsRationale

	NoVartypeOptions = 0
)
View Source
const AutofixFormat = "AutofixFormat"

AutofixFormat is a special value that is used for logging diagnostics like "Replacing \"old\" with \"new\".".

Since these are not really diagnostics, duplicates are not suppressed.

View Source
const SilentAutofixFormat = "SilentAutofixFormat"

SilentAutofixFormat is used in exceptional situations when an autofix action is not directly related to a diagnostic.

To prevent confusion, the code using this magic value must ensure to log a diagnostic by other means.

Variables

View Source
var (
	Fatal           = &LogLevel{"FATAL", "fatal"}
	Error           = &LogLevel{"ERROR", "error"}
	Warn            = &LogLevel{"WARN", "warning"}
	Note            = &LogLevel{"NOTE", "note"}
	AutofixLogLevel = &LogLevel{"AUTOFIX", "autofix"}
)
View Source
var (
	LowerDash = textproc.NewByteSet("a-z---")
	AlnumDot  = textproc.NewByteSet("A-Za-z0-9_.")
)
View Source
var (
	VarbaseBytes  = textproc.NewByteSet("A-Za-z_0-9+---")
	VarparamBytes = textproc.NewByteSet("A-Za-z_0-9#*+---./[")
)

VarbaseBytes contains characters that may be used in the main part of variable names. VarparamBytes contains characters that may be used in the parameter part of variable names.

For example, TOOLS_PATH.[ is a valid variable name but [ alone isn't since the opening bracket is only allowed in the parameter part of variable names.

This approach differs from the one in devel/bmake/files/parse.c:/^Parse_IsVar, but in practice it works equally well. Luckily there aren't many situations where a complicated variable name contains unbalanced parentheses or braces, which would confuse the devel/bmake parser.

TODO: The allowed characters differ between the basename and the parameter

of the variable. The square bracket is only allowed in the parameter part.
View Source
var (
	BtAwkCommand             = &BasicType{"AwkCommand", (*VartypeCheck).AwkCommand}
	BtBasicRegularExpression = &BasicType{"BasicRegularExpression", (*VartypeCheck).BasicRegularExpression}
	BtBuildlinkDepmethod     = &BasicType{"BuildlinkDepmethod", (*VartypeCheck).BuildlinkDepmethod}
	BtCategory               = &BasicType{"Category", (*VartypeCheck).Category}
	BtCFlag                  = &BasicType{"CFlag", (*VartypeCheck).CFlag}
	BtComment                = &BasicType{"Comment", (*VartypeCheck).Comment}
	BtConfFiles              = &BasicType{"ConfFiles", (*VartypeCheck).ConfFiles}
	BtDependency             = &BasicType{"Dependency", (*VartypeCheck).Dependency}
	BtDependencyWithPath     = &BasicType{"DependencyWithPath", (*VartypeCheck).DependencyWithPath}
	BtDistSuffix             = &BasicType{"DistSuffix", (*VartypeCheck).DistSuffix}
	BtEmulPlatform           = &BasicType{"EmulPlatform", (*VartypeCheck).EmulPlatform}
	BtFetchURL               = &BasicType{"FetchURL", (*VartypeCheck).FetchURL}
	BtFileName               = &BasicType{"Filename", (*VartypeCheck).Filename}
	BtFileMask               = &BasicType{"FileMask", (*VartypeCheck).FileMask}
	BtFileMode               = &BasicType{"FileMode", (*VartypeCheck).FileMode}
	BtGccReqd                = &BasicType{"GccReqd", (*VartypeCheck).GccReqd}
	BtHomepage               = &BasicType{"Homepage", (*VartypeCheck).Homepage}
	BtIdentifier             = &BasicType{"Identifier", (*VartypeCheck).Identifier}
	BtInteger                = &BasicType{"Integer", (*VartypeCheck).Integer}
	BtLdFlag                 = &BasicType{"LdFlag", (*VartypeCheck).LdFlag}
	BtLicense                = &BasicType{"License", (*VartypeCheck).License}
	BtMachineGnuPlatform     = &BasicType{"MachineGnuPlatform", (*VartypeCheck).MachineGnuPlatform}
	BtMachinePlatform        = &BasicType{"MachinePlatform", (*VartypeCheck).MachinePlatform}
	BtMachinePlatformPattern = &BasicType{"MachinePlatformPattern", (*VartypeCheck).MachinePlatformPattern}
	BtMailAddress            = &BasicType{"MailAddress", (*VartypeCheck).MailAddress}
	BtMessage                = &BasicType{"Message", (*VartypeCheck).Message}
	BtOption                 = &BasicType{"Option", (*VartypeCheck).Option}
	BtPathlist               = &BasicType{"Pathlist", (*VartypeCheck).Pathlist}
	BtPathmask               = &BasicType{"PathMask", (*VartypeCheck).PathMask}
	BtPathname               = &BasicType{"Pathname", (*VartypeCheck).Pathname}
	BtPerl5Packlist          = &BasicType{"Perl5Packlist", (*VartypeCheck).Perl5Packlist}
	BtPerms                  = &BasicType{"Perms", (*VartypeCheck).Perms}
	BtPkgName                = &BasicType{"Pkgname", (*VartypeCheck).Pkgname}
	BtPkgPath                = &BasicType{"PkgPath", (*VartypeCheck).PkgPath}
	BtPkgOptionsVar          = &BasicType{"PkgOptionsVar", (*VartypeCheck).PkgOptionsVar}
	BtPkgRevision            = &BasicType{"PkgRevision", (*VartypeCheck).PkgRevision}
	BtPrefixPathname         = &BasicType{"PrefixPathname", (*VartypeCheck).PrefixPathname}
	BtPythonDependency       = &BasicType{"PythonDependency", (*VartypeCheck).PythonDependency}
	BtRPkgName               = &BasicType{"RPkgName", (*VartypeCheck).RPkgName}
	BtRPkgVer                = &BasicType{"RPkgVer", (*VartypeCheck).RPkgVer}
	BtRelativePkgDir         = &BasicType{"RelativePkgDir", (*VartypeCheck).RelativePkgDir}
	BtRelativePkgPath        = &BasicType{"RelativePkgPath", (*VartypeCheck).RelativePkgPath}
	BtRestricted             = &BasicType{"Restricted", (*VartypeCheck).Restricted}
	BtSedCommands            = &BasicType{"SedCommands", (*VartypeCheck).SedCommands}
	BtShellCommand           = &BasicType{"ShellCommand", nil}
	BtShellCommands          = &BasicType{"ShellCommands", nil}
	BtShellWord              = &BasicType{"ShellWord", nil}
	BtStage                  = &BasicType{"Stage", (*VartypeCheck).Stage}
	BtTool                   = &BasicType{"Tool", (*VartypeCheck).Tool}
	BtUnknown                = &BasicType{"Unknown", (*VartypeCheck).Unknown}
	BtURL                    = &BasicType{"URL", (*VartypeCheck).URL}
	BtUserGroupName          = &BasicType{"UserGroupName", (*VartypeCheck).UserGroupName}
	BtVariableName           = &BasicType{"VariableName", (*VartypeCheck).VariableName}
	BtVariableNamePattern    = &BasicType{"VariableNamePattern", (*VartypeCheck).VariableNamePattern}
	BtVersion                = &BasicType{"Version", (*VartypeCheck).Version}
	BtWrapperReorder         = &BasicType{"WrapperReorder", (*VartypeCheck).WrapperReorder}
	BtWrapperTransform       = &BasicType{"WrapperTransform", (*VartypeCheck).WrapperTransform}
	BtWrkdirSubdirectory     = &BasicType{"WrkdirSubdirectory", (*VartypeCheck).WrkdirSubdirectory}
	BtWrksrcSubdirectory     = &BasicType{"WrksrcSubdirectory", (*VartypeCheck).WrksrcSubdirectory}
	BtYes                    = &BasicType{"Yes", (*VartypeCheck).Yes}
	BtYesNo                  = &BasicType{"YesNo", (*VartypeCheck).YesNo}
	BtYesNoIndirectly        = &BasicType{"YesNoIndirectly", (*VartypeCheck).YesNoIndirectly}
)
View Source
var (
	G = NewPkglint()
)

G is the abbreviation for "global state"; this and the tracer are the only global variables in this Go package.

Functions

func CheckFileAlternatives

func CheckFileAlternatives(filename string)

func CheckFileMk

func CheckFileMk(filename string)

func CheckFileOther

func CheckFileOther(filename string)

func CheckLinesBuildlink3Mk

func CheckLinesBuildlink3Mk(mklines *MkLines)

func CheckLinesDescr

func CheckLinesDescr(lines *Lines)

func CheckLinesDistinfo

func CheckLinesDistinfo(pkg *Package, lines *Lines)

func CheckLinesMessage

func CheckLinesMessage(lines *Lines)

func CheckLinesOptionsMk

func CheckLinesOptionsMk(mklines *MkLines)

func CheckLinesPatch

func CheckLinesPatch(lines *Lines)

func CheckLinesPlist

func CheckLinesPlist(pkg *Package, lines *Lines)

func CheckLinesTrailingEmptyLines

func CheckLinesTrailingEmptyLines(lines *Lines)

func CheckdirCategory

func CheckdirCategory(dir string)

func CheckdirToplevel

func CheckdirToplevel(dir string)

func IsPrefs

func IsPrefs(filename string) bool

IsPrefs returns whether the given file, when included, loads the user preferences.

func MatchMkInclude

func MatchMkInclude(text string) (m bool, indentation, directive, filename string)

func NewPlistLineSorter

func NewPlistLineSorter(plines []*PlistLine) *plistLineSorter

func SaveAutofixChanges

func SaveAutofixChanges(lines *Lines) (autofixed bool)

SaveAutofixChanges writes the given lines back into their files, applying the autofix changes. The lines may come from different files. Only files that actually have changed lines are saved.

Types

type ACLEntry

type ACLEntry struct {
	// contains filtered or unexported fields
}

func NewACLEntry

func NewACLEntry(glob string, permissions ACLPermissions) ACLEntry

type ACLPermissions

type ACLPermissions uint8

func (ACLPermissions) Contains

func (perms ACLPermissions) Contains(subset ACLPermissions) bool

Contains returns whether each permission of the given subset is contained in this permission set.

func (ACLPermissions) HumanString

func (perms ACLPermissions) HumanString() string

func (ACLPermissions) String

func (perms ACLPermissions) String() string

type Autofix

type Autofix struct {
	// contains filtered or unexported fields
}

Autofix handles all modifications to a single line, describes them in a human-readable form and formats the output. The modifications are kept in memory only, until they are written to disk by SaveAutofixChanges.

func NewAutofix

func NewAutofix(line *Line) *Autofix

func (*Autofix) Anyway

func (fix *Autofix) Anyway()

Anyway has the effect of showing the diagnostic even when nothing can be fixed automatically.

As usual, the diagnostic is only shown if neither --show-autofix nor --autofix mode is given.

func (*Autofix) Apply

func (fix *Autofix) Apply()

Apply does the actual work. Depending on the pkglint mode, it either:

* logs the associated message (default) but does not record the fixes in the line

* logs what would be fixed (--show-autofix) and records the fixes in the line

* records the fixes in the line (--autofix), ready for SaveAutofixChanges

func (*Autofix) Custom

func (fix *Autofix) Custom(fixer func(showAutofix, autofix bool))

Custom runs a custom fix action, unless the fix is skipped anyway because of the --only option.

The fixer function must check whether it can actually fix something, and if so, call Describef to describe the actual fix.

If showAutofix and autofix are both false, the fix must only be described by calling Describef. No observable modification must be done, not even in memory.

If showAutofix is true but autofix is false, the fix should be done in memory as far as possible. For example, changing the text of Line.raw is appropriate, but changing files in the file system is not.

Only if autofix is true, fixes other than modifying the current Line should be done persistently, such as changes to the file system.

In any case, changes to the current Line will be written back to disk by SaveAutofixChanges, after fixing all the lines in the file at once.

func (*Autofix) Delete

func (fix *Autofix) Delete()

Delete removes the current line completely. It can be combined with InsertAfter or InsertBefore to replace the complete line with some different text.

func (*Autofix) Describef

func (fix *Autofix) Describef(lineno int, format string, args ...interface{})

Describef is used while Autofix.Custom is called to remember a description of the actual fix for logging it later when Apply is called. Describef may be called multiple times before calling Apply.

func (*Autofix) Errorf

func (fix *Autofix) Errorf(format string, args ...interface{})

Errorf remembers the error for logging it later when Apply is called.

func (*Autofix) Explain

func (fix *Autofix) Explain(explanation ...string)

Explain remembers the explanation for logging it later when Apply is called.

func (*Autofix) InsertAfter

func (fix *Autofix) InsertAfter(text string)

InsertAfter appends a line after the current line. The newline is added internally.

func (*Autofix) InsertBefore

func (fix *Autofix) InsertBefore(text string)

InsertBefore prepends a line before the current line. The newline is added internally.

func (*Autofix) Notef

func (fix *Autofix) Notef(format string, args ...interface{})

Notef remembers the note for logging it later when Apply is called.

func (*Autofix) Replace

func (fix *Autofix) Replace(from string, to string)

Replace replaces "from" with "to", a single time.

func (*Autofix) ReplaceAfter

func (fix *Autofix) ReplaceAfter(prefix, from string, to string)

ReplaceAfter replaces the text "prefix+from" with "prefix+to", a single time. In the diagnostic, only the replacement of "from" with "to" is mentioned.

func (*Autofix) ReplaceAt

func (fix *Autofix) ReplaceAt(rawIndex int, column int, from string, to string)

ReplaceAt replaces the text "from" with "to", a single time. But only if the text at the given position is indeed "from".

func (*Autofix) ReplaceRegex

func (fix *Autofix) ReplaceRegex(from regex.Pattern, toText string, howOften int)

ReplaceRegex replaces the first howOften or all occurrences (if negative) of the `from` pattern with the fixed string `toText`.

Placeholders like `$1` are _not_ expanded in the `toText`. (If you know how to do the expansion correctly, feel free to implement it.)

func (*Autofix) Warnf

func (fix *Autofix) Warnf(format string, args ...interface{})

Warnf remembers the warning for logging it later when Apply is called.

type BasicType

type BasicType struct {
	// contains filtered or unexported fields
}

func (*BasicType) AllowedEnums

func (bt *BasicType) AllowedEnums() string

func (*BasicType) HasEnum

func (bt *BasicType) HasEnum(value string) bool

func (*BasicType) IsEnum

func (bt *BasicType) IsEnum() bool

func (*BasicType) NeedsQ

func (bt *BasicType) NeedsQ() bool

NeedsQ returns whether variables of this type need the :Q modifier to be safely embedded in other variables or shell programs.

Variables that can consists only of characters like A-Za-z0-9-._ don't need the :Q modifier. All others do, for safety reasons.

type Buildlink3Checker

type Buildlink3Checker struct {
	// contains filtered or unexported fields
}

func (*Buildlink3Checker) Check

func (ck *Buildlink3Checker) Check()

type Change

type Change struct {
	Location Location
	Action   ChangeAction // Added, Updated, Downgraded, Renamed, Moved, Removed
	Pkgpath  string       // For renamed or moved packages, the previous PKGPATH

	Author string
	Date   string
	// contains filtered or unexported fields
}

Change describes a modification to a single package, from the doc/CHANGES-* files.

func (*Change) Above

func (ch *Change) Above(other *Change) bool

func (*Change) Successor

func (ch *Change) Successor() string

Successor returns the successor for a Removed package.

func (*Change) Target

func (ch *Change) Target() string

Target returns the target PKGPATH for a Renamed or Moved package.

func (*Change) Version

func (ch *Change) Version() string

Version returns the version number for an Added, Updated or Downgraded package.

type ChangeAction

type ChangeAction uint8
const (
	Added ChangeAction = 1 + iota
	Updated
	Downgraded
	Renamed
	Moved
	Removed
)

func ParseChangeAction

func ParseChangeAction(s string) ChangeAction

func (ChangeAction) String

func (ca ChangeAction) String() string

type CmdOpts

type CmdOpts struct {
	CheckGlobal bool

	WarnExtra,
	WarnPerm,
	WarnQuoting,
	WarnSpace,
	WarnStyle bool

	Profiling,
	ShowHelp,
	DumpMakefile,
	Import,
	Recursive,
	ShowVersion bool

	LogOnly []string
	// contains filtered or unexported fields
}

type CvsEntry

type CvsEntry struct {
	Name      string
	Revision  string
	Timestamp string
	Options   string
	TagDate   string
}

CvsEntry is one of the entries in a CVS/Entries file.

See http://cvsman.com/cvs-1.12.12/cvs_19.php.

type DependencyPattern

type DependencyPattern struct {
	Pkgbase  string // "freeciv-client", "{gcc48,gcc48-libs}", "${EMACS_REQD}"
	LowerOp  string // ">=", ">"
	Lower    string // "2.5.0", "${PYVER}"
	UpperOp  string // "<", "<="
	Upper    string // "3.0", "${PYVER}"
	Wildcard string // "[0-9]*", "1.5.*", "${PYVER}"
}

type FileCache

type FileCache struct {
	// contains filtered or unexported fields
}

FileCache reduces the IO load for commonly loaded files by about 50%, especially for buildlink3.mk and *.buildlink3.mk files.

func NewFileCache

func NewFileCache(size int) *FileCache

func (*FileCache) Evict

func (c *FileCache) Evict(filename string)

func (*FileCache) Get

func (c *FileCache) Get(filename string, options LoadOptions) *Lines

func (*FileCache) Put

func (c *FileCache) Put(filename string, options LoadOptions, lines *Lines)

type Hash

type Hash struct {
	// contains filtered or unexported fields
}

type Indentation

type Indentation struct {
	// contains filtered or unexported fields
}

Indentation remembers the stack of preprocessing directives and their indentation. By convention, each directive is indented by 2 spaces. An excepting are multiple-inclusion guards, they don't increase the indentation.

Indentation starts with 0 spaces.
Each .if or .for indents all inner directives by 2.
Except for .if with multiple-inclusion guard, which indents all inner directives by 0.
Each .elif, .else, .endif, .endfor uses the outer indentation instead.

func NewIndentation

func NewIndentation() *Indentation

func (*Indentation) AddCheckedFile

func (ind *Indentation) AddCheckedFile(filename string)

func (*Indentation) AddVar

func (ind *Indentation) AddVar(varname string)

AddVar remembers that the current indentation depends on the given variable, most probably because that variable is used in a .if directive.

Variables named *_MK are ignored since they are usually not interesting.

func (*Indentation) Args

func (ind *Indentation) Args() string

Args returns the arguments of the innermost .if, .elif or .for.

func (*Indentation) CheckFinish

func (ind *Indentation) CheckFinish(filename string)

func (*Indentation) DependsOn

func (ind *Indentation) DependsOn(varname string) bool

func (*Indentation) Depth

func (ind *Indentation) Depth(directive string) int

Depth returns the number of space characters by which the directive should be indented.

This is typically two more than the surrounding level, except for multiple-inclusion guards.

func (*Indentation) Empty

func (ind *Indentation) Empty() bool

func (*Indentation) HasExists

func (ind *Indentation) HasExists(filename string) bool

HasExists returns whether the given filename has been tested in an exists(filename) condition and thus may or may not exist.

func (*Indentation) IsConditional

func (ind *Indentation) IsConditional() bool

IsConditional returns whether the current line depends on evaluating any variable in an .if or .elif expression or from a .for loop.

Variables named *_MK are excluded since they are usually not interesting.

func (*Indentation) Pop

func (ind *Indentation) Pop()

func (*Indentation) Push

func (ind *Indentation) Push(mkline *MkLine, indent int, condition string)

func (*Indentation) RememberUsedVariables

func (ind *Indentation) RememberUsedVariables(cond *MkCond)

func (*Indentation) String

func (ind *Indentation) String() string

func (*Indentation) TrackAfter

func (ind *Indentation) TrackAfter(mkline *MkLine)

func (*Indentation) TrackBefore

func (ind *Indentation) TrackBefore(mkline *MkLine)

func (*Indentation) Varnames

func (ind *Indentation) Varnames() []string

Varnames returns the list of all variables that are mentioned in any condition or loop surrounding the current line.

Variables named *_MK are excluded since they are usually not interesting.

type InterPackage

type InterPackage struct {
	// contains filtered or unexported fields
}

func (*InterPackage) Bl3

func (ip *InterPackage) Bl3(name string, loc *Location) *Location

Bl3 remembers that the given buildlink3 name is used at the given location. Since these names must be unique, there should be no other location where the same name is used.

func (*InterPackage) Enable

func (ip *InterPackage) Enable()

func (*InterPackage) Enabled

func (ip *InterPackage) Enabled() bool

func (*InterPackage) Hash

func (ip *InterPackage) Hash(alg, filename string, hashBytes []byte, loc *Location) *Hash

func (*InterPackage) LicenseUsed

func (ip *InterPackage) LicenseUsed(name string) bool

func (*InterPackage) UseLicense

func (ip *InterPackage) UseLicense(name string)

type LicenseChecker

type LicenseChecker struct {
	MkLines *MkLines
	MkLine  *MkLine
}

func (*LicenseChecker) Check

func (lc *LicenseChecker) Check(value string, op MkOperator)

type Line

type Line struct {
	// TODO: Consider storing pointers to the Filename and Basename instead of strings to save memory.
	//  But first find out where and why pkglint needs so much memory (200 MB for a full recursive run over pkgsrc + wip).
	Location
	Basename string // the last component of the Filename

	// the text of the line, without the trailing newline character;
	// in Makefiles, also contains the text from the continuation lines,
	// joined by single spaces
	Text string
	// contains filtered or unexported fields
}

Line represents a line of text from a file.

func NewLine

func NewLine(filename string, lineno int, text string, rawLine *RawLine) *Line

func NewLineEOF

func NewLineEOF(filename string) *Line

NewLineEOF creates a dummy line for logging, with the "line number" EOF.

func NewLineMulti

func NewLineMulti(filename string, firstLine, lastLine int, text string, rawLines []*RawLine) *Line

NewLineMulti is for logical Makefile lines that end with backslash.

func NewLineWhole

func NewLineWhole(filename string) *Line

NewLineWhole creates a dummy line for logging messages that affect a file as a whole.

func (*Line) Autofix

func (line *Line) Autofix() *Autofix

Autofix returns the autofix instance belonging to the line.

Usage:

fix := line.Autofix()

fix.Errorf("Must not be ...")
fix.Warnf("Should not be ...")
fix.Notef("It is also possible ...")

fix.Explain(
    "Explanation ...",
    "... end of explanation.")

fix.Replace("from", "to")
fix.ReplaceAfter("prefix", "from", "to")
fix.ReplaceRegex(`[\t ]+`, "space", -1)
fix.InsertBefore("new line")
fix.InsertAfter("new line")
fix.Delete()
fix.Custom(func(showAutofix, autofix bool) {})

fix.Apply()

func (*Line) Errorf

func (line *Line) Errorf(format string, args ...interface{})

func (*Line) Explain

func (line *Line) Explain(explanation ...string)

func (*Line) Fatalf

func (line *Line) Fatalf(format string, args ...interface{})

func (*Line) IsCvsID

func (line *Line) IsCvsID(prefixRe regex.Pattern) (found bool, expanded bool)

func (*Line) IsMultiline

func (line *Line) IsMultiline() bool

func (*Line) Notef

func (line *Line) Notef(format string, args ...interface{})

func (*Line) PathToFile

func (line *Line) PathToFile(filePath string) string

PathToFile returns the relative path from this line to the given file path. This is typically used for arguments in diagnostics, which should always be relative to the line with which the diagnostic is associated.

func (*Line) RefTo

func (line *Line) RefTo(other *Line) string

RefTo returns a reference to another line, which can be in the same file or in a different file.

func (*Line) RefToLocation

func (line *Line) RefToLocation(other Location) string

func (*Line) String

func (line *Line) String() string

func (*Line) Warnf

func (line *Line) Warnf(format string, args ...interface{})

type LineChecker

type LineChecker struct {
	// contains filtered or unexported fields
}

func (LineChecker) CheckLength

func (ck LineChecker) CheckLength(maxLength int)

func (LineChecker) CheckTrailingWhitespace

func (ck LineChecker) CheckTrailingWhitespace()

func (LineChecker) CheckValidCharacters

func (ck LineChecker) CheckValidCharacters()

type Lines

type Lines struct {
	Filename string
	BaseName string
	Lines    []*Line
}

func Load

func Load(filename string, options LoadOptions) *Lines

func NewLines

func NewLines(filename string, lines []*Line) *Lines

func (*Lines) CheckCvsID

func (ls *Lines) CheckCvsID(index int, prefixRe regex.Pattern, suggestedPrefix string) bool

CheckCvsID returns true if the expected CVS Id was found.

func (*Lines) EOFLine

func (ls *Lines) EOFLine() *Line

func (*Lines) Errorf

func (ls *Lines) Errorf(format string, args ...interface{})

func (*Lines) LastLine

func (ls *Lines) LastLine() *Line

func (*Lines) Len

func (ls *Lines) Len() int

func (*Lines) SaveAutofixChanges

func (ls *Lines) SaveAutofixChanges() bool

func (*Lines) Warnf

func (ls *Lines) Warnf(format string, args ...interface{})

type LinesLexer

type LinesLexer struct {
	// contains filtered or unexported fields
}

LinesLexer records the state when checking a list of lines from top to bottom.

func NewLinesLexer

func NewLinesLexer(lines *Lines) *LinesLexer

func (*LinesLexer) CurrentLine

func (llex *LinesLexer) CurrentLine() *Line

CurrentLine returns the line that the lexer is currently looking at. If it is at the end of file, the line number of the line is EOF.

func (*LinesLexer) EOF

func (llex *LinesLexer) EOF() bool

func (*LinesLexer) NextRegexp

func (llex *LinesLexer) NextRegexp(re regex.Pattern) []string

func (*LinesLexer) PreviousLine

func (llex *LinesLexer) PreviousLine() *Line

func (*LinesLexer) Skip

func (llex *LinesLexer) Skip() bool

Skip skips the current line and returns true.

func (*LinesLexer) SkipContainsOrWarn

func (llex *LinesLexer) SkipContainsOrWarn(text string) bool

func (*LinesLexer) SkipEmptyOrNote

func (llex *LinesLexer) SkipEmptyOrNote() bool

func (*LinesLexer) SkipPrefix

func (llex *LinesLexer) SkipPrefix(prefix string) bool

func (*LinesLexer) SkipRegexp

func (llex *LinesLexer) SkipRegexp(re regex.Pattern) bool

func (*LinesLexer) SkipString

func (llex *LinesLexer) SkipString(text string) bool

func (*LinesLexer) Undo

func (llex *LinesLexer) Undo()

type LoadOptions

type LoadOptions uint8
const (
	MustSucceed LoadOptions = 1 << iota // It's a fatal error if loading fails.
	NotEmpty                            // It is an error if the file is empty.
	Makefile                            // Lines ending in a backslash are continued in the next line.
	LogErrors                           //
)

type Location

type Location struct {
	Filename string // uses / as directory separator on all platforms
	// contains filtered or unexported fields
}

func NewLocation

func NewLocation(filename string, firstLine, lastLine int) Location

func (*Location) Linenos

func (loc *Location) Linenos() string

func (*Location) String

func (loc *Location) String() string

type LogLevel

type LogLevel struct {
	TraditionalName string
	GccName         string
}

type Logger

type Logger struct {
	Opts LoggerOpts
	// contains filtered or unexported fields
}

func (*Logger) Diag

func (l *Logger) Diag(line *Line, level *LogLevel, format string, args ...interface{})

Diag logs a diagnostic. These are filtered by the --only command line option, and duplicates are suppressed unless the --log-verbose command line option is given.

See Logf for logging arbitrary messages.

func (*Logger) Errorf

func (l *Logger) Errorf(location string, format string, args ...interface{})

Errorf logs a technical error on the error output.

location must be a slash-separated filename, such as the one in Location.Filename. It may be followed by the usual ":123" for line numbers.

For diagnostics, use Logf instead.

func (*Logger) Explain

func (l *Logger) Explain(explanation ...string)

Explain outputs an explanation for the preceding diagnostic if the --explain option is given. Otherwise it just records that an explanation is available.

func (*Logger) FirstTime

func (l *Logger) FirstTime(filename, linenos, msg string) bool

func (*Logger) IsAutofix

func (l *Logger) IsAutofix() bool

IsAutofix returns whether one of the --show-autofix or --autofix options is active.

func (*Logger) Logf

func (l *Logger) Logf(level *LogLevel, filename, lineno, format, msg string)

func (*Logger) Relevant

func (l *Logger) Relevant(format string) bool

Relevant decides and remembers whether the given diagnostic is relevant and should be logged.

The result of the decision affects all log items until Relevant is called for the next time.

func (*Logger) ShowSummary

func (l *Logger) ShowSummary()

type LoggerOpts

type LoggerOpts struct {
	ShowAutofix,
	Autofix,
	Explain,
	ShowSource,
	LogVerbose,
	GccOutput,
	Quiet bool
}

type MkCond

type MkCond struct {
	Or  []*MkCond
	And []*MkCond
	Not *MkCond

	Defined string
	Empty   *MkVarUse
	Term    *MkCondTerm
	Compare *MkCondCompare
	Call    *MkCondCall
}

MkCond is a condition in a Makefile, such as ${OPSYS} == NetBSD.

The representation is somewhere between syntactic and semantic. Unnecessary parentheses are omitted in this representation, but !empty(VARNAME) is represented differently from ${VARNAME} != "". For higher level analysis, a unified representation might be better.

func (*MkCond) Walk

func (cond *MkCond) Walk(callback *MkCondCallback)

type MkCondCall

type MkCondCall struct {
	Name string
	Arg  string
}

type MkCondCallback

type MkCondCallback struct {
	Not     func(cond *MkCond)
	Defined func(varname string)
	Empty   func(empty *MkVarUse)
	Compare func(left *MkCondTerm, op string, right *MkCondTerm)
	Call    func(name string, arg string)

	// Var is called for every atomic expression that consists solely
	// of a variable use, possibly enclosed in double quotes, but without
	// any surrounding string literal parts.
	Var func(varuse *MkVarUse)

	// VarUse is called for each variable that is used in some expression.
	VarUse func(varuse *MkVarUse)
}

MkCondCallback defines the actions for walking a Makefile condition using MkCondWalker.Walk.

type MkCondCompare

type MkCondCompare struct {
	Left MkCondTerm
	// For numeric comparison: one of <, <=, ==, !=, >=, >.
	//
	// For string comparison: one of ==, !=.
	//
	// For not-empty test: "".
	Op    string
	Right MkCondTerm
}

type MkCondTerm

type MkCondTerm struct {
	Str string
	Num string
	Var *MkVarUse
}

type MkCondWalker

type MkCondWalker struct{}

func (*MkCondWalker) Walk

func (w *MkCondWalker) Walk(cond *MkCond, callback *MkCondCallback)

type MkLine

type MkLine struct {
	*Line
	// contains filtered or unexported fields
}

MkLine is a line from a Makefile fragment. There are several types of lines. The most common types in pkgsrc are variable assignments, shell commands and directives like .if and .for.

func (*MkLine) Args

func (mkline *MkLine) Args() string

Args returns the arguments from an .if, .ifdef, .ifndef, .elif, .for, .undef.

func (*MkLine) Cond

func (mkline *MkLine) Cond() *MkCond

Cond applies to an .if or .elif line and returns the parsed condition.

If a parse error occurs, it is silently swallowed, returning a best-effort part of the condition, or even nil.

func (*MkLine) ConditionalVars

func (mkline *MkLine) ConditionalVars() []string

ConditionalVars applies to .include lines and is a space-separated list of those variable names on which the inclusion depends. It is initialized later, step by step, when parsing other lines.

func (*MkLine) Directive

func (mkline *MkLine) Directive() string

Directive returns the preprocessing directive, like "if", "for", "endfor", etc.

See matchMkDirective.

func (*MkLine) DirectiveComment

func (mkline *MkLine) DirectiveComment() string

DirectiveComment is the trailing end-of-line comment, typically at a deeply nested .endif or .endfor.

func (*MkLine) ExplainRelativeDirs

func (mkline *MkLine) ExplainRelativeDirs()

func (*MkLine) Fields

func (mkline *MkLine) Fields() []string

Fields applies to variable assignments and .for loops. For variable assignments, it returns the right-hand side, properly split into words. For .for loops, it returns all arguments (including variable names), properly split into words.

func (*MkLine) FirstLineContainsValue

func (mkline *MkLine) FirstLineContainsValue() bool

FirstLineContainsValue returns whether the variable assignment of a multiline contains a textual value in the first line.

VALUE_IN_FIRST_LINE= value \
        starts in first line
NO_VALUE_IN_FIRST_LINE= \
        value starts in second line

func (*MkLine) ForEachUsed

func (mkline *MkLine) ForEachUsed(action func(varUse *MkVarUse, time VucTime))

ForEachUsed calls the action for each variable that is used in the line.

func (*MkLine) HasElseBranch

func (mkline *MkLine) HasElseBranch() bool

func (*MkLine) IncludedFile

func (mkline *MkLine) IncludedFile() string

func (*MkLine) Indent

func (mkline *MkLine) Indent() string

func (*MkLine) IsComment

func (mkline *MkLine) IsComment() bool

IsComment returns true for lines that consist entirely of a comment.

func (*MkLine) IsCommentedVarassign

func (mkline *MkLine) IsCommentedVarassign() bool

IsCommentedVarassign returns true for commented-out variable assignments. In most cases these are treated as ordinary comments, but in some others they are treated like variable assignments, just inactive ones.

To qualify as a commented variable assignment, there must be no space between the # and the variable name.

Example:

#VAR=   value

Counterexample:

# VAR=  value

func (*MkLine) IsDependency

func (mkline *MkLine) IsDependency() bool

IsDependency returns true for dependency lines like "target: source".

func (*MkLine) IsDirective

func (mkline *MkLine) IsDirective() bool

IsDirective returns true for conditionals (.if/.elif/.else/.if) or loops (.for/.endfor).

See IsInclude.

func (*MkLine) IsEmpty

func (mkline *MkLine) IsEmpty() bool

func (*MkLine) IsInclude

func (mkline *MkLine) IsInclude() bool

IsInclude returns true for lines like: .include "other.mk"

See IsSysinclude for lines like: .include <sys.mk>

func (*MkLine) IsShellCommand

func (mkline *MkLine) IsShellCommand() bool

IsShellCommand returns true for tab-indented lines that are assigned to a Make target. Example:

pre-configure:    # IsDependency
        ${ECHO}   # IsShellCommand

func (*MkLine) IsSysinclude

func (mkline *MkLine) IsSysinclude() bool

IsSysinclude returns true for lines like: .include <sys.mk>

See IsInclude for lines like: .include "other.mk"

func (*MkLine) IsVarassign

func (mkline *MkLine) IsVarassign() bool

IsVarassign returns true for variable assignments of the form VAR=value.

See IsCommentedVarassign.

func (*MkLine) IsVarassignMaybeCommented

func (mkline *MkLine) IsVarassignMaybeCommented() bool

IsVarassignMaybeCommented returns true for variable assignments of the form VAR=value, no matter if they are commented out like #VAR=value or not. To qualify as a commented variable assignment, there must be no space between the # and the variable name.

Example:

#VAR=   value

Counterexample:

# VAR=  value

func (*MkLine) MustExist

func (mkline *MkLine) MustExist() bool

func (*MkLine) Op

func (mkline *MkLine) Op() MkOperator

Op applies to variable assignments and returns the assignment operator.

func (*MkLine) RefTo

func (mkline *MkLine) RefTo(other *MkLine) string

RefTo returns a reference to another line, which can be in the same file or in a different file.

If there is a type mismatch when calling this function, try to add ".line" to either the method receiver or the other line.

func (*MkLine) ResolveVarsInRelativePath

func (mkline *MkLine) ResolveVarsInRelativePath(relativePath string) string

func (*MkLine) SetConditionalVars

func (mkline *MkLine) SetConditionalVars(varnames []string)

func (*MkLine) SetHasElseBranch

func (mkline *MkLine) SetHasElseBranch(elseLine *MkLine)

func (*MkLine) ShellCommand

func (mkline *MkLine) ShellCommand() string

func (*MkLine) Sources

func (mkline *MkLine) Sources() string

func (*MkLine) String

func (mkline *MkLine) String() string

String returns the filename and line numbers.

func (*MkLine) Targets

func (mkline *MkLine) Targets() string

func (*MkLine) Tokenize

func (mkline *MkLine) Tokenize(text string, warn bool) []*MkToken

Tokenize extracts variable uses and other text from the given text.

When used in IsVarassign lines, the given text must have the format after stripping the end-of-line comment. Such text is available from Value. A shell comment is therefore marked by a simple #, not an escaped \# like in Makefiles.

When used in IsShellCommand lines, # does not mark a Makefile comment and may thus still appear in the text. Therefore, # marks a shell comment.

Example:

input:  ${PREFIX}/bin abc
output: [MkToken("${PREFIX}", MkVarUse("PREFIX")), MkToken("/bin abc")]

See ValueTokens, which is the tokenized version of Value.

func (*MkLine) UnquoteShell

func (*MkLine) UnquoteShell(str string) string

func (*MkLine) Value

func (mkline *MkLine) Value() string

func (*MkLine) ValueAlign

func (mkline *MkLine) ValueAlign() string

ValueAlign applies to variable assignments and returns all the text before the variable value, e.g. "VARNAME+=\t".

func (*MkLine) ValueFields

func (mkline *MkLine) ValueFields(value string) []string

ValueFields splits the given value in the same way as the :M variable modifier, taking care of variable references. Example:

ValueFields("${VAR:Udefault value} ${VAR2}two words;;; 'word three'")
=> "${VAR:Udefault value}"
   "${VAR2}two"
   "words;;;"
   "'word three'"

Note that even though the first word contains a space, it is not split at that point since the space is inside a variable use. Shell tokens such as semicolons are also treated as normal characters. Only double and single quotes are interpreted.

Compare devel/bmake/files/str.c, function brk_string.

TODO: Compare with brk_string from devel/bmake, especially for backticks.

func (*MkLine) ValueSplit

func (mkline *MkLine) ValueSplit(value string, separator string) []string

ValueSplit splits the given value, taking care of variable references. Example:

ValueSplit("${VAR:Udefault}::${VAR2}two:words", ":")
=> "${VAR:Udefault}"
   ""
   "${VAR2}two"
   "words"

Note that even though the first word contains a colon, it is not split at that point since the colon is inside a variable use.

When several separators are adjacent, this results in empty words in the output.

func (*MkLine) ValueTokens

func (mkline *MkLine) ValueTokens() ([]*MkToken, string)

func (*MkLine) VarassignComment

func (mkline *MkLine) VarassignComment() string

VarassignComment applies to variable assignments and returns the comment.

Example:

VAR=value # comment

In the above line, the comment is "# comment".

The leading "#" is included so that pkglint can distinguish between no comment at all and an empty comment.

func (*MkLine) Varcanon

func (mkline *MkLine) Varcanon() string

Varcanon applies to variable assignments and returns the canonicalized variable name for parameterized variables. Examples:

HOMEPAGE           => "HOMEPAGE"
SUBST_SED.anything => "SUBST_SED.*"
SUBST_SED.${param} => "SUBST_SED.*"

func (*MkLine) VariableNeedsQuoting

func (mkline *MkLine) VariableNeedsQuoting(mklines *MkLines, varuse *MkVarUse, vartype *Vartype, vuc *VarUseContext) (needsQuoting YesNoUnknown)

VariableNeedsQuoting determines whether the given variable needs the :Q operator in the given context.

This decision depends on many factors, such as whether the type of the context is a list of things, whether the variable is a list, whether it can contain only safe characters, and so on.

func (*MkLine) Varname

func (mkline *MkLine) Varname() string

Varname applies to variable assignments and returns the name of the variable that is assigned or appended to.

Example:

VARNAME.${param}?=      value   # Varname is "VARNAME.${param}"

func (*MkLine) Varparam

func (mkline *MkLine) Varparam() string

Varparam applies to variable assignments and returns the parameter for parameterized variables. Examples:

HOMEPAGE           => ""
SUBST_SED.anything => "anything"
SUBST_SED.${param} => "${param}"

func (*MkLine) WithoutMakeVariables

func (*MkLine) WithoutMakeVariables(value string) string

type MkLineChecker

type MkLineChecker struct {
	MkLines *MkLines
	MkLine  *MkLine
}

MkLineChecker provides checks for a single line from a Makefile fragment.

func (MkLineChecker) Check

func (ck MkLineChecker) Check()

func (MkLineChecker) CheckRelativePath

func (ck MkLineChecker) CheckRelativePath(relativePath string, mustExist bool)

CheckRelativePath checks a relative path that leads to the directory of another package or to a subdirectory thereof or a file within there.

func (MkLineChecker) CheckRelativePkgdir

func (ck MkLineChecker) CheckRelativePkgdir(pkgdir string)

CheckRelativePkgdir checks a reference from one pkgsrc package to another. These references should always have the form ../../category/package.

When used in DEPENDS or similar variables, these directories could theoretically also be relative to the pkgsrc root, which would save a few keystrokes. This, however, is not implemented in pkgsrc and suggestions regarding this topic have not been made in the last two decades on the public mailing lists. While being a bit redundant, the current scheme works well.

When used in .include directives, the relative package directories must be written with the leading ../.. anyway, so the benefit might not be too big at all.

func (MkLineChecker) CheckVartypeBasic

func (ck MkLineChecker) CheckVartypeBasic(varname string, checker *BasicType, op MkOperator, value, comment string, guessed bool)

CheckVartypeBasic checks a single list element of the given type.

For some variables (like `BuildlinkDepth`), `op` influences the valid values. The `comment` parameter comes from a variable assignment, when a part of the line is commented out.

func (MkLineChecker) CheckVaruse

func (ck MkLineChecker) CheckVaruse(varuse *MkVarUse, vuc *VarUseContext)

CheckVaruse checks a single use of a variable in a specific context.

type MkLineParser

type MkLineParser struct{}

func (MkLineParser) MatchVarassign

func (p MkLineParser) MatchVarassign(line *Line, text string) (bool, *mkLineAssign)

func (MkLineParser) Parse

func (p MkLineParser) Parse(line *Line) *MkLine

Parse parses the text of a Makefile line to see what kind of line it is: variable assignment, include, comment, etc.

See devel/bmake/parse.c:/^Parse_File/

type MkLines

type MkLines struct {
	Tools *Tools // Tools defined in file scope.
	// contains filtered or unexported fields
}

MkLines contains data for the Makefile (or *.mk) that is currently checked.

func LoadMk

func LoadMk(filename string, options LoadOptions) *MkLines

func NewMkLines

func NewMkLines(lines *Lines) *MkLines

func (*MkLines) Check

func (mklines *MkLines) Check()

func (*MkLines) CheckUsedBy

func (mklines *MkLines) CheckUsedBy(relativeName string)

CheckUsedBy checks that this file (a Makefile.common) has the given relativeName in one of the "# used by" comments at the beginning of the file.

func (*MkLines) EOFLine

func (mklines *MkLines) EOFLine() *MkLine

func (*MkLines) ExpandLoopVar

func (mklines *MkLines) ExpandLoopVar(varname string) []string

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) ForEach

func (mklines *MkLines) ForEach(action func(mkline *MkLine))

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) ForEachEnd

func (mklines *MkLines) ForEachEnd(action func(mkline *MkLine) bool, atEnd func(lastMkline *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) SaveAutofixChanges

func (mklines *MkLines) SaveAutofixChanges()

func (*MkLines) SplitToParagraphs

func (mklines *MkLines) SplitToParagraphs() []*Paragraph

func (*MkLines) UseVar

func (mklines *MkLines) UseVar(mkline *MkLine, varname string, time VucTime)

UseVar remembers that the given variable is used in the given line. This controls the "defined but not used" warning.

type MkLinesLexer

type MkLinesLexer struct {
	LinesLexer
	// contains filtered or unexported fields
}

MkLinesLexer records the state when checking a list of Makefile lines from top to bottom.

func NewMkLinesLexer

func NewMkLinesLexer(mklines *MkLines) *MkLinesLexer

func (*MkLinesLexer) CurrentMkLine

func (mlex *MkLinesLexer) CurrentMkLine() *MkLine

func (*MkLinesLexer) PreviousMkLine

func (mlex *MkLinesLexer) PreviousMkLine() *MkLine

func (*MkLinesLexer) SkipIf

func (mlex *MkLinesLexer) SkipIf(pred func(mkline *MkLine) bool) bool

func (*MkLinesLexer) SkipWhile

func (mlex *MkLinesLexer) SkipWhile(pred func(mkline *MkLine) bool)

type MkOperator

type MkOperator uint8

func NewMkOperator

func NewMkOperator(op string) MkOperator

func (MkOperator) String

func (op MkOperator) String() string

func (MkOperator) Time

func (op MkOperator) Time() VucTime

Time returns the time at which the right-hand side of the assignment is evaluated.

type MkParser

type MkParser struct {
	Line *Line

	EmitWarnings bool
	// contains filtered or unexported fields
}

MkParser wraps a Parser and provides methods for parsing things related to Makefiles.

func NewMkParser

func NewMkParser(line *Line, text string) *MkParser

NewMkParser creates a new parser for the given text.

If line is given, it is used for reporting parse errors and warnings. Otherwise parsing is silent.

The text argument is assumed to be after unescaping the # character, which means the # is a normal character and does not introduce a Makefile comment. For VarUse, this distinction is irrelevant.

func (*MkParser) Dependency

func (p *MkParser) Dependency() *DependencyPattern

Dependency parses a dependency pattern like "pkg>=1<2" or "pkg-[0-9]*".

func (*MkParser) EOF

func (p *MkParser) EOF() bool

func (*MkParser) MkCond

func (p *MkParser) MkCond() *MkCond

MkCond parses a condition like ${OPSYS} == "NetBSD".

See devel/bmake/files/cond.c.

func (*MkParser) MkTokens

func (p *MkParser) MkTokens() []*MkToken

MkTokens splits a text like in the following example:

Text${VAR:Mmodifier}${VAR2}more text${VAR3}

into tokens like these:

Text
${VAR:Mmodifier}
${VAR2}
more text
${VAR3}

func (*MkParser) Op

func (p *MkParser) Op() (bool, MkOperator)

func (*MkParser) PkgbasePattern

func (p *MkParser) PkgbasePattern() string

func (*MkParser) Rest

func (p *MkParser) Rest() string

func (*MkParser) VarUse

func (p *MkParser) VarUse() *MkVarUse

func (*MkParser) VarUseModifiers

func (p *MkParser) VarUseModifiers(varname string, closing byte) []MkVarUseModifier

VarUseModifiers parses the modifiers of a variable being used, such as :Q, :Mpattern.

See the bmake manual page.

func (*MkParser) Varname

func (p *MkParser) Varname() string

type MkShAndOr

type MkShAndOr struct {
	Pipes []*MkShPipeline
	Ops   []string // Each element is either "&&" or "||"
}

MkShAndOr is a group of commands that are connected with && or || conditions.

The operators && and || have the same precedence and are evaluated strictly from left to right.

Example:

cd $dir && echo "In $dir" || echo "Cannot cd into $dir"

func NewMkShAndOr

func NewMkShAndOr(pipeline *MkShPipeline) *MkShAndOr

func (*MkShAndOr) Add

func (andor *MkShAndOr) Add(op string, pipeline *MkShPipeline) *MkShAndOr

type MkShCase

type MkShCase struct {
	Word  *ShToken
	Cases []*MkShCaseItem
}

MkShCase is a "case" statement, including all its branches.

Example:

case $filename in *.c) echo "C source" ;; esac

type MkShCaseItem

type MkShCaseItem struct {
	Patterns  []*ShToken
	Action    *MkShList
	Separator MkShSeparator
	Var       *ShToken // ${PATTERNS:@p@ (${p}) action ;; @}
}

MkShCaseItem is one branch of a "case" statement.

Example:

*.c) echo "C source" ;;

type MkShCommand

type MkShCommand struct {
	Simple    *MkShSimpleCommand
	Compound  *MkShCompoundCommand
	FuncDef   *MkShFunctionDefinition
	Redirects []*MkShRedirection // For Compound and FuncDef
}

MkShCommand is a simple or compound shell command.

Examples:

LC_ALL=C sort */*.c > sorted
dir() { ls -l "$@"; }
{ echo "first"; echo "second"; }

type MkShCompoundCommand

type MkShCompoundCommand struct {
	Brace    *MkShList
	Subshell *MkShList
	For      *MkShFor
	Case     *MkShCase
	If       *MkShIf
	Loop     *MkShLoop
}

MkShCompoundCommand is a group of commands.

Examples:

{ echo "first"; echo "second"; }
for f in *.c; do compile "$f"; done
if [ -f "$file" ]; then echo "It exists"; fi
while sleep 1; do printf .; done

type MkShFor

type MkShFor struct {
	Varname string
	Values  []*ShToken
	Body    *MkShList
}

MkShFor is a "for" loop.

Example:

for f in *.c; do compile "$f"; done

type MkShFunctionDefinition

type MkShFunctionDefinition struct {
	Name string
	Body *MkShCompoundCommand
}

MkShFunctionDefinition is the definition of a shell function.

Example:

dir() { ls -l "$@"; }

type MkShIf

type MkShIf struct {
	Conds   []*MkShList
	Actions []*MkShList
	Else    *MkShList
}

MkShIf is a conditional statement, possibly having many branches.

Example:

if [ -f "$file" ]; then echo "It exists"; fi

func (*MkShIf) Prepend

func (cl *MkShIf) Prepend(cond *MkShList, action *MkShList)

type MkShList

type MkShList struct {
	AndOrs []*MkShAndOr

	// The separators after each AndOr.
	// There may be one less entry than in AndOrs.
	Separators []MkShSeparator
}

MkShList is a list of shell commands, separated by newlines or semicolons.

Example:

cd $dir && echo "In $dir"; cd ..; ls -l

func NewMkShList

func NewMkShList() *MkShList

func (*MkShList) AddAndOr

func (list *MkShList) AddAndOr(andor *MkShAndOr) *MkShList

func (*MkShList) AddSeparator

func (list *MkShList) AddSeparator(separator MkShSeparator) *MkShList

type MkShLoop

type MkShLoop struct {
	Cond   *MkShList
	Action *MkShList
	Until  bool
}

MkShLoop is a "while" or "until" loop.

Example:

while sleep 1; do printf .; done

type MkShPipeline

type MkShPipeline struct {
	Negated bool
	Cmds    []*MkShCommand
}

MkShPipeline is a group of commands, connected by pipelines.

Example: grep word file | sed s,^,---,

func NewMkShPipeline

func NewMkShPipeline(negated bool, cmds []*MkShCommand) *MkShPipeline

func (*MkShPipeline) Add

func (pipe *MkShPipeline) Add(cmd *MkShCommand) *MkShPipeline

type MkShRedirection

type MkShRedirection struct {
	Fd     int      // Or -1
	Op     string   // See io_file in shell.y for possible values
	Target *ShToken // The filename or &fd
}

MkShRedirection is a single file descriptor redirection.

Examples:

> sorted
2>&1

type MkShSeparator

type MkShSeparator uint8

MkShSeparator is one of ; & newline.

type MkShSimpleCommand

type MkShSimpleCommand struct {
	Assignments  []*ShToken
	Name         *ShToken
	Args         []*ShToken
	Redirections []*MkShRedirection
}

MkShSimpleCommand is a shell command that does not involve any pipeline or conditionals.

Example:

LC_ALL=C sort */*.c > sorted

type MkShWalker

type MkShWalker struct {
	Callback struct {
		List               func(list *MkShList)
		AndOr              func(andor *MkShAndOr)
		Pipeline           func(pipeline *MkShPipeline)
		Command            func(command *MkShCommand)
		SimpleCommand      func(command *MkShSimpleCommand)
		CompoundCommand    func(command *MkShCompoundCommand)
		Case               func(caseClause *MkShCase)
		CaseItem           func(caseItem *MkShCaseItem)
		FunctionDefinition func(funcdef *MkShFunctionDefinition)
		If                 func(ifClause *MkShIf)
		Loop               func(loop *MkShLoop)
		Words              func(words []*ShToken)
		Word               func(word *ShToken)
		Redirects          func(redirects []*MkShRedirection)
		Redirect           func(redirect *MkShRedirection)
		For                func(forClause *MkShFor)

		// For variable definition in a for loop.
		Varname func(varname string)
	}

	// Context[0] is the currently visited element,
	// Context[1] is its immediate parent element, and so on.
	// This is useful when the check for a CaseItem needs to look at the enclosing Case.
	Context []MkShWalkerPathElement
}

func NewMkShWalker

func NewMkShWalker() *MkShWalker

func (*MkShWalker) Current

func (w *MkShWalker) Current() MkShWalkerPathElement

Current provides access to the element that the walker is currently processing, especially its index as seen from its parent element.

func (*MkShWalker) Parent

func (w *MkShWalker) Parent(steps int) interface{}

Parent returns an ancestor element from the currently visited path. Parent(0) is the element that is currently visited, Parent(1) is its direct parent, and so on.

func (*MkShWalker) Path

func (w *MkShWalker) Path() string

Path returns a representation of the path in the AST that is currently visited.

It is used for debugging only.

See Test_MkShWalker_Walk, Callback.SimpleCommand for examples.

func (*MkShWalker) Walk

func (w *MkShWalker) Walk(list *MkShList)

Walk calls the given callback for each node of the parsed shell program, in visiting order from large to small.

type MkShWalkerPathElement

type MkShWalkerPathElement struct {

	// For fields that can be repeated, this is the index as seen from the parent element.
	// For fields that cannot be repeated, it is -1.
	//
	// For example, in the SimpleCommand "var=value cmd arg1 arg2",
	// there are multiple child elements of type Words.
	//
	// The first Words are the variable assignments, which have index 0.
	//
	// The command "cmd" has type Word, therefore it cannot be confused
	// with either of the Words lists and has index -1.
	//
	// The second Words are the arguments, which have index 1.
	// In this example, there are two arguments, so when visiting the
	// arguments individually, arg1 will have index 0 and arg2 will have index 1.
	//
	// TODO: It might be worth defining negative indexes to correspond
	//  to the fields "Cond", "Action", "Else", etc.
	Index int

	Element interface{}
}

type MkToken

type MkToken struct {
	Text   string    // Used for both literal text and variable uses
	Varuse *MkVarUse // For literal text, it is nil
}

MkToken represents a contiguous string from a Makefile. It is either a literal string or a variable use.

Example: /usr/share/${PKGNAME}/data consists of 3 tokens:

  1. MkToken{Text: "/usr/share/"}
  2. MkToken{Text: "${PKGNAME}", Varuse: &MkVarUse{varname: "PKGNAME"}}
  3. MkToken{Text: "/data"}

type MkTokensLexer

type MkTokensLexer struct {
	// The lexer for the current text-only token.
	// If the current token is a variable use, the lexer will always return
	// EOF internally. That is not visible from the outside though, as EOF is
	// overridden in this type.
	*textproc.Lexer
	// contains filtered or unexported fields
}

MkTokensLexer parses a sequence of variable uses (like ${VAR:Mpattern}) interleaved with other text that is uninterpreted by bmake.

func NewMkTokensLexer

func NewMkTokensLexer(tokens []*MkToken) *MkTokensLexer

func (*MkTokensLexer) EOF

func (m *MkTokensLexer) EOF() bool

EOF returns whether the whole input has been consumed.

func (*MkTokensLexer) Mark

func (m *MkTokensLexer) Mark() MkTokensLexerMark

Mark remembers the current position of the lexer. The lexer can later be reset to that position by calling Reset.

func (*MkTokensLexer) NextVarUse

func (m *MkTokensLexer) NextVarUse() *MkVarUse

NextVarUse returns the next varuse token, unless there is some plain text before it. In that case or at EOF, it returns nil.

func (*MkTokensLexer) Reset

func (m *MkTokensLexer) Reset(mark MkTokensLexerMark)

Reset sets the lexer back to the given position. The lexer may be reset to the same mark multiple times, that is, the mark is not destroyed.

func (*MkTokensLexer) Rest

func (m *MkTokensLexer) Rest() string

Rest returns the string concatenation of the tokens that have not yet been consumed.

func (*MkTokensLexer) Since

func (m *MkTokensLexer) Since(mark MkTokensLexerMark) string

Since returns the text between the given mark and the current position of the lexer.

type MkTokensLexerMark

type MkTokensLexerMark struct {
	// contains filtered or unexported fields
}

MkTokensLexerMark remembers a position of a token lexer, to be restored later.

type MkVarUse

type MkVarUse struct {
	// contains filtered or unexported fields
}

MkVarUse represents a reference to a Make variable, with optional modifiers.

For nested variable expressions, the variable name can contain references to other variables. For example, ${TOOLS.${t}} is a MkVarUse with varname "TOOLS.${t}" and no modifiers.

Example: ${PKGNAME}

Example: ${PKGNAME:S/from/to/}

func ToVarUse

func ToVarUse(str string) *MkVarUse

ToVarUse converts the given string into a MkVarUse, or returns nil if there is a parse error or some trailing text. Parse errors are silently ignored.

func (*MkVarUse) IsExpression

func (vu *MkVarUse) IsExpression() bool

IsExpression returns whether the varname is interpreted as an expression instead of a variable name (rare, only the modifiers :? and :L do this).

func (*MkVarUse) IsQ

func (vu *MkVarUse) IsQ() bool

func (*MkVarUse) Mod

func (vu *MkVarUse) Mod() string

func (*MkVarUse) String

func (vu *MkVarUse) String() string

type MkVarUseModifier

type MkVarUseModifier struct {
	Text string
}

func (MkVarUseModifier) ChangesWords

func (m MkVarUseModifier) ChangesWords() bool

ChangesWords returns true if applying this modifier to a list variable may change the number of words in the list, or their boundaries.

func (MkVarUseModifier) IsQ

func (m MkVarUseModifier) IsQ() bool

func (MkVarUseModifier) IsSuffixSubst

func (m MkVarUseModifier) IsSuffixSubst() bool

func (MkVarUseModifier) IsToLower

func (m MkVarUseModifier) IsToLower() bool

func (MkVarUseModifier) MatchMatch

func (m MkVarUseModifier) MatchMatch() (ok bool, positive bool, pattern string, exact bool)

MatchMatch tries to match the modifier to a :M or a :N pattern matching. Examples:

:Mpattern   => true, true, "pattern"
:Npattern   => true, false, "pattern"
:X          => false

func (MkVarUseModifier) MatchSubst

func (m MkVarUseModifier) MatchSubst() (ok bool, regex bool, from string, to string, options string)

func (MkVarUseModifier) Subst

func (m MkVarUseModifier) Subst(str string) (string, bool)

Subst evaluates an S/from/to/ modifier.

Example:

MkVarUseModifier{"S,name,file,g"}.Subst("distname-1.0") => "distfile-1.0"

type Once

type Once struct {

	// Only used during testing, to trace the actual arguments,
	// since hashing is a one-way function.
	Trace bool
	// contains filtered or unexported fields
}

Once remembers with which arguments its FirstTime method has been called and only returns true on each first call.

func (*Once) FirstTime

func (o *Once) FirstTime(what string) bool

func (*Once) FirstTimeSlice

func (o *Once) FirstTimeSlice(whats ...string) bool

func (*Once) Seen

func (o *Once) Seen(what string) bool

type OptionsLinesChecker

type OptionsLinesChecker struct {
	// contains filtered or unexported fields
}

OptionsLinesChecker checks an options.mk file of a pkgsrc package.

See mk/bsd.options.mk for a detailed description.

func (*OptionsLinesChecker) Check

func (ck *OptionsLinesChecker) Check()

type Package

type Package struct {
	Pkgpath              string       // e.g. "category/pkgdir"
	Pkgdir               string       // PKGDIR from the package Makefile
	Filesdir             string       // FILESDIR from the package Makefile
	Patchdir             string       // PATCHDIR from the package Makefile
	DistinfoFile         string       // DISTINFO_FILE from the package Makefile
	EffectivePkgname     string       // PKGNAME or DISTNAME from the package Makefile, including nb13
	EffectivePkgbase     string       // EffectivePkgname without the version
	EffectivePkgversion  string       // The version part of the effective PKGNAME, excluding nb13
	EffectivePkgnameLine *MkLine      // The origin of the three Effective* values
	Plist                PlistContent // Files and directories mentioned in the PLIST files

	IgnoreMissingPatches bool // In distinfo, don't warn about patches that cannot be found.
	// contains filtered or unexported fields
}

Package is the pkgsrc package that is currently checked.

Most of the information is loaded first, and after loading the actual checks take place. This is necessary because variables in Makefiles may be used before they are defined, and such dependencies often span multiple files that are included indirectly.

func NewPackage

func NewPackage(dir string) *Package

func (*Package) AutofixDistinfo

func (pkg *Package) AutofixDistinfo(oldSha1, newSha1 string)

func (*Package) CheckVarorder

func (pkg *Package) CheckVarorder(mklines *MkLines)

CheckVarorder checks that in simple package Makefiles, the most common variables appear in a fixed order. The order itself is a little arbitrary but provides at least a bit of consistency.

func (*Package) File

func (pkg *Package) File(relativeFileName string) string

File returns the (possibly absolute) path to relativeFileName, as resolved from the package's directory. Variables that are known in the package are resolved, e.g. ${PKGDIR}.

func (*Package) Rel

func (pkg *Package) Rel(filename string) string

Rel returns the path by which the given filename (as seen from the current working directory) can be reached as a relative path from the package directory.

Example:

NewPackage("category/package").Rel("other/package") == "../../other/package"

type Paragraph

type Paragraph struct {
	// contains filtered or unexported fields
}

Paragraph is a slice of Makefile lines that is surrounded by empty lines.

All variable assignments in a paragraph should be aligned in the same column.

If the paragraph adds an identifier to SUBST_CLASSES, the rest of the SUBST block should be defined in the same paragraph.

func NewParagraph

func NewParagraph(mklines *MkLines, from, to int) *Paragraph

func (*Paragraph) Align

func (p *Paragraph) Align()

func (*Paragraph) AlignTo

func (p *Paragraph) AlignTo(column int)

AlignTo realigns all variable assignments in the paragraph so that their values start in the same column. Variable assignments that are commented out are also realigned.

No warning or note is logged. Therefore this method must only be used to realign the whole paragraph after one of its lines has been modified.

func (*Paragraph) FirstLine

func (p *Paragraph) FirstLine() *MkLine

func (*Paragraph) ForEach

func (p *Paragraph) ForEach(action func(mkline *MkLine))

func (*Paragraph) LastLine

func (p *Paragraph) LastLine() *MkLine

func (*Paragraph) MkLines

func (p *Paragraph) MkLines() []*MkLine

type ParseError

type ParseError struct {
	RemainingTokens []string
}

func (*ParseError) Error

func (e *ParseError) Error() string

type PatchChecker

type PatchChecker struct {
	// contains filtered or unexported fields
}

func (*PatchChecker) Check

func (ck *PatchChecker) Check()

type Pkglint

type Pkglint struct {
	Opts   CmdOpts  // Command line options.
	Pkgsrc Pkgsrc   // Global data, mostly extracted from mk/*.
	Pkg    *Package // The package that is currently checked, or nil.

	Todo           []string // The files or directories that still need to be checked.
	Wip            bool     // Is the currently checked file or package from pkgsrc-wip?
	Infrastructure bool     // Is the currently checked file from the pkgsrc infrastructure?
	Testing        bool     // Is pkglint in self-testing mode (only during development)?
	Username       string   // For checking against OWNER and MAINTAINER

	Logger Logger

	InterPackage InterPackage
	// contains filtered or unexported fields
}

Pkglint is a container for all global variables of this Go package.

func NewPkglint

func NewPkglint() Pkglint

func (*Pkglint) Check

func (pkglint *Pkglint) Check(dirent string)

Check checks a directory entry, which can be a regular file, a directory or a symlink (only allowed for the working directory).

This is the method that is called for each command line argument.

It sets up all the global state (infrastructure, wip) for accurately classifying the entry.

During tests, it assumes that Pkgsrc.LoadInfrastructure has been called. It is the most high-level method for testing pkglint.

func (*Pkglint) Main

func (pkglint *Pkglint) Main(stdout io.Writer, stderr io.Writer, args []string) (exitCode int)

Main runs the main program with the given arguments. args[0] is the program name.

Note: during tests, calling this method disables tracing because the getopt parser resets all options before the actual parsing. One of these options is trace.Tracing, which is connected to --debug.

It also discards the -Wall option that is used by default in other tests.

func (*Pkglint) ParseCommandLine

func (pkglint *Pkglint) ParseCommandLine(args []string) int

func (*Pkglint) Tool

func (pkglint *Pkglint) Tool(mklines *MkLines, command string, time ToolTime) (tool *Tool, usable bool)

Tool returns the tool definition from the closest scope (file, global), or nil. The command can be "sed" or "gsed" or "${SED}". If a tool is returned, usable tells whether that tool has been added to USE_TOOLS in the current scope (file or package).

func (*Pkglint) ToolByVarname

func (pkglint *Pkglint) ToolByVarname(mklines *MkLines, varname string) *Tool

ToolByVarname looks up the tool by its variable name, e.g. "SED".

The returned tool may come either from the current file or the current package. It is not guaranteed to be usable (added to USE_TOOLS), only defined; that must be checked by the calling code, see Tool.UsableAtLoadTime and Tool.UsableAtRunTime.

type Pkgsrc

type Pkgsrc struct {
	Tools *Tools

	MasterSiteURLToVar map[string]string // "https://github.com/" => "MASTER_SITE_GITHUB"
	MasterSiteVarToURL map[string]string // "MASTER_SITE_GITHUB" => "https://github.com/"

	PkgOptions map[string]string // "x11" => "Provides X11 support"

	LastChange      map[string]*Change
	LastFreezeStart string // e.g. "2018-01-01", or ""
	LastFreezeEnd   string // e.g. "2018-01-01", or ""

	// Variables that may be overridden by the pkgsrc user.
	// They are typically defined in mk/defaults/mk.conf.
	//
	// Whenever a package uses such a variable, it must add the variable name
	// to BUILD_DEFS.
	UserDefinedVars Scope

	Deprecated map[string]string
	// contains filtered or unexported fields
}

Pkgsrc describes a pkgsrc installation. In each pkglint run, only a single pkgsrc installation is ever loaded. It just doesn't make sense to check multiple pkgsrc installations at once.

This type only contains data that is loaded once and then stays constant. Everything else (distfile hashes, package names) is recorded in the Pkglint type instead.

func NewPkgsrc

func NewPkgsrc(dir string) Pkgsrc

func (*Pkgsrc) File

func (src *Pkgsrc) File(relativeName string) string

File resolves a filename relative to the pkgsrc top directory.

Example:

NewPkgsrc("/usr/pkgsrc").File("distfiles") => "/usr/pkgsrc/distfiles"

func (*Pkgsrc) IsBuildDef

func (src *Pkgsrc) IsBuildDef(varname string) bool

IsBuildDef returns whether the given variable is automatically added to BUILD_DEFS by the pkgsrc infrastructure. In such a case, the package doesn't need to add the variable to BUILD_DEFS itself.

func (*Pkgsrc) IsInfra

func (src *Pkgsrc) IsInfra(filename string) bool

IsInfra returns whether the given filename (relative to the pkglint working directory) is part of the pkgsrc infrastructure.

func (*Pkgsrc) Latest

func (src *Pkgsrc) Latest(category string, re regex.Pattern, repl string) string

Latest returns the latest package matching the given pattern. It searches the category for subdirectories matching the given regular expression, takes the latest of them and replaces its name with repl.

Example:

Latest("lang", `^php[0-9]+$`, "../../lang/$0")
    => "../../lang/php72"

func (*Pkgsrc) ListVersions

func (src *Pkgsrc) ListVersions(category string, re regex.Pattern, repl string, errorIfEmpty bool) []string

ListVersions searches the category for subdirectories matching the given regular expression, replaces their names with repl and returns a slice of them, properly sorted from early to late.

Example:

ListVersions("lang", `^php[0-9]+$`, "php-$0")
    => {"php-53", "php-56", "php-73"}

func (*Pkgsrc) Load

func (src *Pkgsrc) Load(filename string, options LoadOptions) *Lines

Load loads the file relative to the pkgsrc top directory.

func (*Pkgsrc) LoadInfrastructure

func (src *Pkgsrc) LoadInfrastructure()

LoadInfrastructure reads the pkgsrc infrastructure files to extract information like the tools, packages to update, user-defined variables.

This work is not done in the constructor to keep the tests simple, since setting up a realistic pkgsrc environment requires a lot of files.

func (*Pkgsrc) LoadMk

func (src *Pkgsrc) LoadMk(filename string, options LoadOptions) *MkLines

LoadMk loads the Makefile relative to the pkgsrc top directory.

func (*Pkgsrc) ReadDir

func (src *Pkgsrc) ReadDir(dirName string) []os.FileInfo

ReadDir lists the files and subdirectories from the given directory (relative to the pkgsrc root), filtering out any ignored files (CVS/*) and empty directories.

func (*Pkgsrc) SuggestedUpdates

func (src *Pkgsrc) SuggestedUpdates() []SuggestedUpdate

func (*Pkgsrc) ToRel

func (src *Pkgsrc) ToRel(filename string) string

ToRel returns the path of `filename`, relative to the pkgsrc top directory.

Example:

NewPkgsrc("/usr/pkgsrc").ToRel("/usr/pkgsrc/distfiles") => "distfiles"

func (*Pkgsrc) VariableType

func (src *Pkgsrc) VariableType(mklines *MkLines, varname string) (vartype *Vartype)

VariableType returns the type of the variable (possibly guessed based on the variable name), or nil if the type cannot even be guessed.

type PlistChecker

type PlistChecker struct {
	// contains filtered or unexported fields
}

func (*PlistChecker) Check

func (ck *PlistChecker) Check(plainLines *Lines)

func (*PlistChecker) Load

func (ck *PlistChecker) Load(lines *Lines) []*PlistLine

func (*PlistChecker) NewLines

func (ck *PlistChecker) NewLines(lines *Lines) []*PlistLine

type PlistContent

type PlistContent struct {
	Dirs  map[string]*PlistLine
	Files map[string]*PlistLine
}

PlistContent lists the directories and files that appear in the package's PLIST files. It serves two purposes:

1. Decide whether AUTO_MKDIRS can be used instead of listing the INSTALLATION_DIRS redundantly.

2. Ensure that the entries mentioned in the ALTERNATIVES file also appear in the PLIST files.

func NewPlistContent

func NewPlistContent() PlistContent

type PlistLine

type PlistLine struct {
	*Line
	// contains filtered or unexported fields
}

func (*PlistLine) CheckDirective

func (pline *PlistLine) CheckDirective(cmd, arg string)

func (*PlistLine) CheckTrailingWhitespace

func (pline *PlistLine) CheckTrailingWhitespace()

type RawLine

type RawLine struct {
	Lineno int // Counting starts at 1
	// contains filtered or unexported fields
}

func (*RawLine) String

func (rline *RawLine) String() string

type RedundantScope

type RedundantScope struct {
	// contains filtered or unexported fields
}

RedundantScope checks for redundant variable definitions and for variables that are accidentally overwritten. It tries to be as correct as possible by not flagging anything that is defined conditionally.

There may be some edge cases though like defining PKGNAME, then evaluating it using :=, then defining it again. This pattern is so error-prone that it should not appear in pkgsrc at all, thus pkglint doesn't even expect it. (Well, except for the PKGNAME case, but that's deep in the infrastructure and only affects the "nb13" extension.)

TODO: This scope is not only used for detecting redundancies. It also provides information about whether the variables are constant or depend on other variables. Therefore the name may change soon.

func NewRedundantScope

func NewRedundantScope() *RedundantScope

func (*RedundantScope) Check

func (s *RedundantScope) Check(mklines *MkLines)

type Scope

type Scope struct {
	// contains filtered or unexported fields
}

Scope remembers which variables are defined and which are used in a certain scope, such as a package or a file.

TODO: Decide whether the scope should consider variable assignments

from the pkgsrc infrastructure. For Package.checkGnuConfigureUseLanguages
it would be better to ignore them completely.

TODO: Merge this code with Var, which defines essentially the

same features.

func NewScope

func NewScope() Scope

func (*Scope) Commented

func (s *Scope) Commented(varname string) *MkLine

Commented returns whether the variable has only been defined in commented variable assignments. These are ignored by bmake but used heavily in mk/defaults/mk.conf for documentation.

func (*Scope) Define

func (s *Scope) Define(varname string, mkline *MkLine)

Define marks the variable and its canonicalized form as defined.

func (*Scope) DefineAll

func (s *Scope) DefineAll(other Scope)

func (*Scope) Defined

func (s *Scope) Defined(varname string) bool

Defined tests whether the variable is defined. It does NOT test the canonicalized variable name.

Even if Defined returns true, FirstDefinition doesn't necessarily return true since the latter ignores the default definitions from vardefs.go, keyword dummyVardefMkline.

func (*Scope) DefinedSimilar

func (s *Scope) DefinedSimilar(varname string) bool

DefinedSimilar tests whether the variable or its canonicalized form is defined.

func (*Scope) Fallback

func (s *Scope) Fallback(varname string, value string)

func (*Scope) FirstDefinition

func (s *Scope) FirstDefinition(varname string) *MkLine

FirstDefinition returns the line in which the variable has been defined first.

Having multiple definitions is typical in the branches of "if" statements.

Another typical case involves two files: the included file defines a default value, and the including file later overrides that value. Or the other way round: the including file sets a value first, and the included file then assigns a default value using ?=.

func (*Scope) FirstUse

func (s *Scope) FirstUse(varname string) *MkLine

func (*Scope) LastDefinition

func (s *Scope) LastDefinition(varname string) *MkLine

LastDefinition returns the line in which the variable has been defined last.

Having multiple definitions is typical in the branches of "if" statements.

Another typical case involves two files: the included file defines a default value, and the including file later overrides that value. Or the other way round: the including file sets a value first, and the included file then assigns a default value using ?=.

func (*Scope) LastValue

func (s *Scope) LastValue(varname string) string

LastValue returns the value from the last variable definition.

If an empty string is returned this can mean either that the variable value is indeed the empty string or that the variable was not found. To distinguish these cases, call LastValueFound instead.

func (*Scope) LastValueFound

func (s *Scope) LastValueFound(varname string) (value string, found bool)

func (*Scope) Mentioned

func (s *Scope) Mentioned(varname string) *MkLine

Mentioned returns the first line in which the variable is either:

  • defined,
  • mentioned in a commented variable assignment,
  • mentioned in a documentation comment.

func (*Scope) Use

func (s *Scope) Use(varname string, line *MkLine, time VucTime)

Use marks the variable and its canonicalized form as used.

func (*Scope) Used

func (s *Scope) Used(varname string) bool

Used tests whether the variable is used. It does NOT test the canonicalized variable name.

func (*Scope) UsedAtLoadTime

func (s *Scope) UsedAtLoadTime(varname string) bool

UsedAtLoadTime returns true if the variable is used at load time somewhere.

func (*Scope) UsedSimilar

func (s *Scope) UsedSimilar(varname string) bool

UsedSimilar tests whether the variable or its canonicalized form is used.

type SeparatorWriter

type SeparatorWriter struct {
	// contains filtered or unexported fields
}

SeparatorWriter writes output, occasionally separated by an empty line. This is used for separating the diagnostics when --source is combined with --show-autofix, where each log message consists of multiple lines.

func NewSeparatorWriter

func NewSeparatorWriter(out io.Writer) *SeparatorWriter

func (*SeparatorWriter) Flush

func (wr *SeparatorWriter) Flush()

func (*SeparatorWriter) Separate

func (wr *SeparatorWriter) Separate()

Separate remembers to output an empty line before the next character. If the writer is currently in the middle of a line, that line is terminated immediately.

func (*SeparatorWriter) Write

func (wr *SeparatorWriter) Write(text string)

func (*SeparatorWriter) WriteLine

func (wr *SeparatorWriter) WriteLine(text string)

type ShAtom

type ShAtom struct {
	Type    ShAtomType
	MkText  string
	Quoting ShQuoting // The quoting state at the end of the token
	// contains filtered or unexported fields
}

func (*ShAtom) ShVarname

func (atom *ShAtom) ShVarname() string

ShVarname applies to shell variable atoms like $$varname or $${varname:-modifier} and returns the name of the shell variable.

func (*ShAtom) String

func (atom *ShAtom) String() string

func (*ShAtom) VarUse

func (atom *ShAtom) VarUse() *MkVarUse

VarUse returns a read access to a Makefile variable, or nil for plain shell tokens.

type ShAtomType

type ShAtomType uint8

func (ShAtomType) IsWord

func (t ShAtomType) IsWord() bool

IsWord checks whether the atom counts as text. Makefile variables, shell variables and other text counts, but keywords, operators and separators don't.

func (ShAtomType) String

func (t ShAtomType) String() string

type ShQuoting

type ShQuoting uint8

ShQuoting describes the context in which a string appears and how it must be unescaped to get its literal value.

func (ShQuoting) String

func (q ShQuoting) String() string

String returns a very short identifier for the quoting state. In this, d means double quotes, s means single quotes, b means backticks and S means subshell.

func (ShQuoting) ToVarUseContext

func (q ShQuoting) ToVarUseContext() VucQuoting

type ShToken

type ShToken struct {
	MkText string // The text as it appeared in the Makefile, after replacing `\#` with `#`
	Atoms  []*ShAtom
}

ShToken is an operator or a keyword or some text intermingled with variables.

Examples:

;
then
"The number of pkgsrc packages in ${PREFIX} is $$packages."

See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_10_02

func NewShToken

func NewShToken(mkText string, atoms ...*ShAtom) *ShToken

func (*ShToken) String

func (token *ShToken) String() string

type ShTokenizer

type ShTokenizer struct {
	// contains filtered or unexported fields
}

func NewShTokenizer

func NewShTokenizer(line *Line, text string, emitWarnings bool) *ShTokenizer

func (*ShTokenizer) Rest

func (p *ShTokenizer) Rest() string

func (*ShTokenizer) ShAtom

func (p *ShTokenizer) ShAtom(quoting ShQuoting) *ShAtom

ShAtom parses a basic building block of a shell program. Examples for such atoms are: variable reference (both make and shell), operator, text, quote, space.

See ShQuote.Feed

func (*ShTokenizer) ShAtoms

func (p *ShTokenizer) ShAtoms() []*ShAtom

func (*ShTokenizer) ShToken

func (p *ShTokenizer) ShToken() *ShToken

type ShellLexer

type ShellLexer struct {
	// contains filtered or unexported fields
}

ShellLexer categorizes tokens for shell commands, providing the lexer required by the yacc-generated parser.

The main work of tokenizing is done in ShellTokenizer though.

Example:

while :; do var=$$other; done

=>

while
space " "
word ":"
semicolon
space " "
do
space " "
assign "var=$$other"
semicolon
space " "
done

See splitIntoShellTokens and ShellTokenizer.

func NewShellLexer

func NewShellLexer(tokens []string, rest string) *ShellLexer

func (*ShellLexer) Error

func (lex *ShellLexer) Error(s string)

func (*ShellLexer) Lex

func (lex *ShellLexer) Lex(lval *shyySymType) (ttype int)

type ShellLineChecker

type ShellLineChecker struct {
	MkLines *MkLines
	// contains filtered or unexported fields
}

ShellLineChecker is either a line from a Makefile starting with a tab, thereby containing shell commands to be executed.

Or it is a variable assignment line from a Makefile with a left-hand side variable that is of some shell-like type; see Vartype.IsShell.

func NewShellLineChecker

func NewShellLineChecker(mklines *MkLines, mkline *MkLine) *ShellLineChecker

func (*ShellLineChecker) CheckShellCommand

func (ck *ShellLineChecker) CheckShellCommand(shellcmd string, pSetE *bool, time ToolTime)

func (*ShellLineChecker) CheckShellCommandLine

func (ck *ShellLineChecker) CheckShellCommandLine(shelltext string)

func (*ShellLineChecker) CheckShellCommands

func (ck *ShellLineChecker) CheckShellCommands(shellcmds string, time ToolTime)

func (*ShellLineChecker) CheckWord

func (ck *ShellLineChecker) CheckWord(token string, checkQuoting bool, time ToolTime)

func (*ShellLineChecker) Explain

func (ck *ShellLineChecker) Explain(explanation ...string)

func (*ShellLineChecker) Warnf

func (ck *ShellLineChecker) Warnf(format string, args ...interface{})

type ShellProgramChecker

type ShellProgramChecker struct {
	*ShellLineChecker
}

func (*ShellProgramChecker) Errorf

func (spc *ShellProgramChecker) Errorf(format string, args ...interface{})

func (*ShellProgramChecker) Explain

func (spc *ShellProgramChecker) Explain(explanation ...string)

func (*ShellProgramChecker) Warnf

func (spc *ShellProgramChecker) Warnf(format string, args ...interface{})

type SimpleCommandChecker

type SimpleCommandChecker struct {
	*ShellLineChecker
	// contains filtered or unexported fields
}

func NewSimpleCommandChecker

func NewSimpleCommandChecker(ck *ShellLineChecker, cmd *MkShSimpleCommand, time ToolTime) *SimpleCommandChecker

func (*SimpleCommandChecker) Check

func (scc *SimpleCommandChecker) Check()

func (*SimpleCommandChecker) Errorf

func (scc *SimpleCommandChecker) Errorf(format string, args ...interface{})

func (*SimpleCommandChecker) Explain

func (scc *SimpleCommandChecker) Explain(explanation ...string)

func (*SimpleCommandChecker) Notef

func (scc *SimpleCommandChecker) Notef(format string, args ...interface{})

func (*SimpleCommandChecker) Warnf

func (scc *SimpleCommandChecker) Warnf(format string, args ...interface{})

type StrCommand

type StrCommand struct {
	Assignments []string
	Name        string
	Args        []string
}

StrCommand is structurally similar to MkShSimpleCommand, but all components are converted to strings to allow for simpler checks, especially for analyzing command line options.

Example:

LC_ALL=C sort */*.c > sorted

func NewStrCommand

func NewStrCommand(cmd *MkShSimpleCommand) *StrCommand

func (*StrCommand) AnyArgMatches

func (c *StrCommand) AnyArgMatches(pattern regex.Pattern) bool

func (*StrCommand) HasOption

func (c *StrCommand) HasOption(opt string) bool

HasOption checks whether one of the arguments is exactly the given opt.

func (*StrCommand) String

func (c *StrCommand) String() string

type StringInterner

type StringInterner struct {
	// contains filtered or unexported fields
}

StringInterner collects commonly used strings to avoid wasting heap memory by duplicated strings.

func NewStringInterner

func NewStringInterner() StringInterner

func (*StringInterner) Intern

func (si *StringInterner) Intern(str string) string

type StringSet

type StringSet struct {
	Elements []string
	// contains filtered or unexported fields
}

StringSet stores unique strings in insertion order.

func NewStringSet

func NewStringSet() StringSet

func (*StringSet) Add

func (s *StringSet) Add(element string)

func (*StringSet) AddAll

func (s *StringSet) AddAll(elements []string)

type SubstContext

type SubstContext struct {
	// contains filtered or unexported fields
}

SubstContext records the state of a block of variable assignments that make up a SUBST class (see `mk/subst.mk`).

func NewSubstContext

func NewSubstContext() *SubstContext

func (*SubstContext) Directive

func (ctx *SubstContext) Directive(mkline *MkLine)

func (*SubstContext) Finish

func (ctx *SubstContext) Finish(mkline *MkLine)

func (*SubstContext) IsComplete

func (ctx *SubstContext) IsComplete() bool

func (*SubstContext) Process

func (ctx *SubstContext) Process(mkline *MkLine)

func (*SubstContext) Varassign

func (ctx *SubstContext) Varassign(mkline *MkLine)

type SubstContextStats

type SubstContextStats struct {
	// contains filtered or unexported fields
}

func (*SubstContextStats) And

func (st *SubstContextStats) And(other *SubstContextStats)

func (*SubstContextStats) Copy

func (*SubstContextStats) Or

func (st *SubstContextStats) Or(other SubstContextStats)

type SuggestedUpdate

type SuggestedUpdate struct {
	Line    Location
	Pkgname string
	Version string
	Comment string
}

SuggestedUpdate describes a desired package update, from the doc/TODO file.

type Tool

type Tool struct {
	Name    string // e.g. "sed", "gzip"
	Varname string // e.g. "SED", "GZIP_CMD"

	// Some of the very simple tools (echo, printf, test) differ in their implementations.
	//
	// When bmake encounters a "simple" command line, it bypasses the
	// call to a shell (see devel/bmake/files/compat.c:/useShell/).
	// Therefore, sometimes the shell builtin is run, and sometimes the
	// native tool.
	//
	// In particular, this decision depends on PKG_DEBUG_LEVEL
	// since that variable adds a semicolon to the command line, which is
	// considered one of the characters that force the commands being
	// executed by the shell. As of December 2018, the list of special characters
	// is "~#=|^(){};&<>*?[]:$`\\\n".
	//
	// To work around this tricky situation, pkglint warns when these shell builtins
	// are used by their simple names (echo, test) instead of the variable form
	// (${ECHO}, ${TEST}).
	MustUseVarForm bool
	Validity       Validity
	Aliases        []string
}

Tool is one of the many standard shell utilities that are typically provided by the operating system, or, if missing, are installed via pkgsrc.

See `mk/tools/`.

func (*Tool) String

func (tool *Tool) String() string

func (*Tool) UsableAtLoadTime

func (tool *Tool) UsableAtLoadTime(seenPrefs bool) bool

UsableAtLoadTime means that the tool may be used by its variable name after bsd.prefs.mk has been included.

Additionally, all allowed cases from UsableAtRunTime are allowed.

VAR:=   ${TOOL}           # Not allowed since bsd.prefs.mk is not
                          # included yet.

.include "../../bsd.prefs.mk"

VAR:=   ${TOOL}           # Allowed.
VAR!=   ${TOOL}           # Allowed.

VAR=    ${${TOOL}:sh}     # Allowed; the :sh modifier is evaluated
                          # lazily, but when VAR should ever be
                          # evaluated at load time, this still means
                          # load time.

.if ${TOOL:T} == "tool"   # Allowed.
.endif

func (*Tool) UsableAtRunTime

func (tool *Tool) UsableAtRunTime() bool

UsableAtRunTime means that the tool may be used by its simple name in all {pre,do,post}-* targets, and by its variable name in all runtime contexts.

VAR:=   ${TOOL}           # Not allowed; TOOL might not be initialized yet.
VAR!=   ${TOOL}           # Not allowed; TOOL might not be initialized yet.

VAR=    ${${TOOL}:sh}     # Probably ok; the :sh modifier is evaluated at
                          # run time. But if VAR should ever be evaluated
                          # at load time (see the "Not allowed" cases
                          # above), it doesn't work. As of January 2019,
                          # pkglint cannot reliably distinguish these cases.

own-target:
        ${TOOL}           # Allowed.
        tool              # Not allowed because the PATH might not be set
                          # up for this target.

pre-configure:
        ${TOOL}           # Allowed.
        tool              # Allowed.

type ToolTime

type ToolTime uint8
const (
	LoadTime ToolTime = iota
	RunTime
)

func (ToolTime) String

func (t ToolTime) String() string

type Tools

type Tools struct {

	// Determines the effect of adding the tool to USE_TOOLS.
	//
	// As long as bsd.prefs.mk has definitely not been included by the current file,
	// tools added to USE_TOOLS are available at load time, but only after bsd.prefs.mk
	// has been included.
	//
	// Adding a tool to USE_TOOLS _after_ bsd.prefs.mk has been included, on the other
	// hand, only makes the tool available at run time.
	SeenPrefs bool

	// For example, "sed" is an alias of "gsed".
	//
	// This means when gsed is added to USE_TOOLS, sed is implicitly added as well.
	AliasOf map[string]string
	// contains filtered or unexported fields
}

Tools collects all tools for a certain scope (global or file) and remembers whether these tools are defined at all, and whether they are declared to be used via USE_TOOLS.

func NewTools

func NewTools() *Tools

func (*Tools) ByName

func (tr *Tools) ByName(name string) *Tool

func (*Tools) ByVarname

func (tr *Tools) ByVarname(varname string) *Tool

func (*Tools) Define

func (tr *Tools) Define(name, varname string, mkline *MkLine) *Tool

Define registers the tool by its name and the corresponding variable name (if nonempty). Depending on the given mkline, it may be added to USE_TOOLS automatically.

After this tool is added to USE_TOOLS, it may be used by this name (e.g. "awk") or by its variable (e.g. ${AWK}).

func (*Tools) Fallback

func (tr *Tools) Fallback(other *Tools)

func (*Tools) IsValidToolName

func (tr *Tools) IsValidToolName(name string) bool

func (*Tools) ParseToolLine

func (tr *Tools) ParseToolLine(mklines *MkLines, mkline *MkLine, fromInfrastructure bool, addToUseTools bool)

ParseToolLine updates the tool definitions according to the given line from a Makefile.

If fromInfrastructure is true, the tool is defined even when it is only added to USE_TOOLS (which normally doesn't define anything). This way, pkglint also finds those tools whose definitions are too difficult to parse from the code.

If addToUseTools is true, a USE_TOOLS line makes a tool immediately usable. This should only be done if the current line is unconditional.

func (*Tools) Trace

func (tr *Tools) Trace()

func (*Tools) Usable

func (tr *Tools) Usable(tool *Tool, time ToolTime) bool

type Toplevel

type Toplevel struct {
	// contains filtered or unexported fields
}

type Validity

type Validity uint8
const (
	// Nowhere means that the tool has not been added
	// to USE_TOOLS and therefore cannot be used at all.
	Nowhere Validity = iota

	// AtRunTime means that the tool has been added to USE_TOOLS
	// after including bsd.prefs.mk and therefore cannot be used
	// at load time.
	//
	// The tool may be used as ${TOOL} in all targets.
	// The tool may be used by its plain name in {pre,do,post}-* targets.
	AtRunTime

	// AfterPrefsMk means that the tool has been added to USE_TOOLS
	// before including bsd.prefs.mk and therefore can be used at
	// load time after bsd.prefs.mk has been included.
	//
	// The tool may be used as ${TOOL} everywhere.
	// The tool may be used by its plain name in {pre,do,post}-* targets.
	AfterPrefsMk
)

func (Validity) String

func (time Validity) String() string

type Var

type Var struct {
	Name string
	// contains filtered or unexported fields
}

Var describes a variable in a Makefile snippet.

It keeps track of all places where the variable is accessed or modified (see ReadLocations, WriteLocations) and provides information for further static analysis, such as:

* Whether the variable value is constant, and if so, what the constant value is (see Constant, ConstantValue).

* What its (approximated) value is, either including values from the pkgsrc infrastructure (see ValueInfra) or excluding them (Value).

* On which other variables this variable depends (see Conditional, ConditionalVars).

TODO: Decide how to handle OPSYS-specific variables, such as LDFLAGS.SunOS.

TODO: Decide how to handle parameterized variables, such as SUBST_MESSAGE.doc.

func NewVar

func NewVar(name string) *Var

func (*Var) AddRef

func (v *Var) AddRef(varname string)

AddRef marks this variable as being dependent on the given variable name. This can be used for the .for loops mentioned in Refs.

func (*Var) Conditional

func (v *Var) Conditional() bool

Conditional returns whether the variable value depends on other variables.

func (*Var) ConditionalVars

func (v *Var) ConditionalVars() []string

ConditionalVars returns all variables in conditions on which the value of this variable depends.

The returned slice must not be modified.

func (*Var) Constant

func (v *Var) Constant() bool

Constant returns whether the variable's value is a constant. It may reference other variables since these references are evaluated lazily, when the variable value is actually needed.

Multiple assignments (such as VAR=1, VAR+=2, VAR+=3) are considered to form a single constant as well, as long as the variable is not read before or in-between these assignments. The definition of "read" is very strict here since every mention of the variable counts. This may prevent some essentially constant values from being detected as such, but these special cases may be implemented later.

TODO: Simple .for loops that append to the variable are ok as well.

(This needs to be worded more precisely since that part potentially
adds a lot of complexity to the whole data structure.)

Variable assignments in the pkgsrc infrastructure are taken into account for determining whether a variable is constant.

func (*Var) ConstantValue

func (v *Var) ConstantValue() string

ConstantValue returns the constant value of the variable. It is only allowed when Constant() returns true.

Variable assignments in the pkgsrc infrastructure are taken into account for determining the constant value.

func (*Var) Read

func (v *Var) Read(mkline *MkLine)

func (*Var) ReadLocations

func (v *Var) ReadLocations() []*MkLine

ReadLocations returns the locations where the variable is read, such as in ${VAR} or defined(VAR) or empty(VAR).

Uses inside conditionals are included, no matter whether they are actually reachable in practice.

Indirect uses through other variables (such as VAR2=${VAR}, VAR3=${VAR2}) are not listed.

Variable uses in the pkgsrc infrastructure are taken into account.

func (*Var) Refs

func (v *Var) Refs() []string

Refs returns all variables on which this variable depends. These are:

Variables that are referenced in the value, such as in VAR=${OTHER}.

Variables that are used in conditions that enclose one of the assignments to this variable, such as .if ${OPSYS} == NetBSD.

Variables that are used in .for loops in which this variable is assigned a value, such as DIRS in:

.for dir in ${DIRS}
VAR+=${dir}
.endfor

func (*Var) Value

func (v *Var) Value() string

Value returns the (approximated) value of the variable, taking into account all variable assignments that happen outside the pkgsrc infrastructure.

For variables that are conditionally assigned (as in .if/.else), the returned value is not reliable. It may be the value from either branch, or even the combined value of both branches.

See Constant and ConstantValue for more reliable information.

func (*Var) ValueInfra

func (v *Var) ValueInfra() string

ValueInfra returns the (approximated) value of the variable, taking into account all variable assignments from the package, the user and the pkgsrc infrastructure.

For variables that are conditionally assigned (as in .if/.else), the returned value is not reliable. It may be the value from either branch, or even the combined value of both branches.

See Constant and ConstantValue for more reliable information, but these ignore assignments from the infrastructure.

func (*Var) Write

func (v *Var) Write(mkline *MkLine, conditional bool, conditionVarnames ...string)

Write marks the variable as being assigned in the given line. Only standard assignments (VAR=value) are handled. Side-effect assignments (${VAR::=value}) are not handled here since they don't occur in practice.

func (*Var) WriteLocations

func (v *Var) WriteLocations() []*MkLine

WriteLocations returns the locations where the variable is modified.

Assignments inside conditionals are included, no matter whether they are actually reachable in practice.

Variable assignments in the pkgsrc infrastructure are taken into account.

type VarTypeRegistry

type VarTypeRegistry struct {
	// contains filtered or unexported fields
}

func NewVarTypeRegistry

func NewVarTypeRegistry() VarTypeRegistry

func (*VarTypeRegistry) Canon

func (reg *VarTypeRegistry) Canon(varname string) *Vartype

func (*VarTypeRegistry) Define

func (reg *VarTypeRegistry) Define(varname string, basicType *BasicType, options vartypeOptions, aclEntries ...ACLEntry)

func (*VarTypeRegistry) DefineParse

func (reg *VarTypeRegistry) DefineParse(varname string, basicType *BasicType, options vartypeOptions, aclEntries ...string)

DefineParse defines a variable with the given type and permissions.

A permission entry looks like this:

"Makefile, Makefile.*, *.mk: default, set, append, use, use-loadtime"

Only certain filenames are allowed in the part before the colon, to prevent typos. To use arbitrary filenames, prefix them with "special:".

TODO: To be implemented: when prefixed with "infra:", the entry only

applies to files within the pkgsrc infrastructure. Without this prefix,
the pattern only applies to files outside the pkgsrc infrastructure.

func (*VarTypeRegistry) DefineType

func (reg *VarTypeRegistry) DefineType(varcanon string, vartype *Vartype)

func (*VarTypeRegistry) DefinedCanon

func (reg *VarTypeRegistry) DefinedCanon(varname string) bool

func (*VarTypeRegistry) DefinedExact

func (reg *VarTypeRegistry) DefinedExact(varname string) bool

func (*VarTypeRegistry) Init

func (reg *VarTypeRegistry) Init(src *Pkgsrc)

Init initializes the long list of predefined pkgsrc variables. After this is done, PKGNAME, MAKE_ENV and all the other variables can be used in Makefiles without triggering warnings about typos.

type VarUseContext

type VarUseContext struct {
	IsWordPart bool // Example: LOCALBASE=${LOCALBASE}
	// contains filtered or unexported fields
}

VarUseContext defines the context in which a variable is defined or used. Whether that is allowed depends on:

* The variable's data type, as defined in vardefs.go.

* When used on the right-hand side of an assigment, the variable can represent a list of words, a single word or even only part of a word. This distinction decides upon the correct use of the :Q operator.

* When used in preprocessing statements like .if or .for, the other operands of that statement should fit to the variable and are checked against the variable type. For example, comparing OPSYS to x86_64 doesn't make sense.

func (*VarUseContext) String

func (vuc *VarUseContext) String() string

type VaralignBlock

type VaralignBlock struct {
	// contains filtered or unexported fields
}

VaralignBlock checks that all variable assignments from a paragraph use the same indentation depth for their values. It also checks that the indentation uses tabs instead of spaces.

In general, all values should be aligned using tabs. As an exception, a single very long line (called an outlier) may be aligned with a single space. A typical example is a SITES.very-long-file-name.tar.gz variable between HOMEPAGE and DISTFILES.

Continuation lines are also aligned to the single-line assignments. There are two types of continuation lines. The first type is an empty multiline:

MULTI_EMPTY_LINE= \
        The value starts in the second line.

The backslash in the first line is usually aligned to the other variables in the same paragraph. If the variable name is so long that it is an outlier, it may be indented with a single space, just like a single-line variable. In multi-line shell commands or AWK programs, the backslash is often indented to column 73, as are the backslashes from the follow-up lines, to act as a visual guideline.

Since this type is often used for URLs or other long values, the first follow-up line may be indented with a single tab, even if the other variables in the paragraph are aligned further to the right. If the indentation is not a single tab, it must match the indentation of the other lines in the paragraph.

INITIAL_LINE=   The value starts in the first line \
                and continues in the second line.

In lists or plain text, like in the INITIAL_LINE above, all values are aligned in the same column. Some variables also contain code, and in these variables, the line containing the first word defines how deep the follow-up lines must be indented at least.

SHELL_CMD=                                                              \
        if ${PKG_ADMIN} pmatch ${PKGNAME} ${dependency}; then           \
                ${ECHO} yes;                                            \
        else                                                            \
                ${ECHO} no;                                             \
        fi

In the continuation lines, each follow-up line is indented with at least one tab, to avoid confusing them with regular single-lines. This is especially true for CONFIGURE_ENV, since the environment variables are typically uppercase as well.

TODO: An initial line has this form:

comment? varname+op space? value? space? comment? space? backslash?

TODO: A follow-up line has the form:

comment? space? value? space? comment? space? backslash?

TODO: The alignment checks are performed on the raw lines instead of

the logical lines, since this check is about the visual appearance
as opposed to the meaning of the variable assignment.

FIXME: Implement each requirement from the above documentation.

func (*VaralignBlock) Finish

func (va *VaralignBlock) Finish()

func (*VaralignBlock) Process

func (va *VaralignBlock) Process(mkline *MkLine)

type Vartype

type Vartype struct {
	// contains filtered or unexported fields
}

Vartype is a combination of a data type and a permission specification. See vardefs.go for examples, and vartypecheck.go for the implementation.

func NewVartype

func NewVartype(basicType *BasicType, options vartypeOptions, aclEntries ...ACLEntry) *Vartype

func (*Vartype) AlternativeFiles

func (vt *Vartype) AlternativeFiles(perms ACLPermissions) string

AlternativeFiles lists the file patterns in which all of the given permissions are allowed, readily formatted to be used in a diagnostic.

If the permission is allowed nowhere, an empty string is returned.

func (*Vartype) CommandLineProvided

func (vt *Vartype) CommandLineProvided() bool

func (*Vartype) EffectivePermissions

func (vt *Vartype) EffectivePermissions(basename string) ACLPermissions

func (*Vartype) Guessed

func (vt *Vartype) Guessed() bool

func (*Vartype) IsShell

func (vt *Vartype) IsShell() bool

func (*Vartype) List

func (vt *Vartype) List() bool

func (*Vartype) MayBeAppendedTo

func (vt *Vartype) MayBeAppendedTo() bool

func (*Vartype) NeedsRationale

func (vt *Vartype) NeedsRationale() bool

func (*Vartype) PackageSettable

func (vt *Vartype) PackageSettable() bool

func (*Vartype) String

func (vt *Vartype) String() string

func (*Vartype) SystemProvided

func (vt *Vartype) SystemProvided() bool

func (*Vartype) Union

func (vt *Vartype) Union() ACLPermissions

Union returns the union of all possible permissions. This can be used to check whether a variable may be defined or used at all, or if it is read-only.

func (*Vartype) UserSettable

func (vt *Vartype) UserSettable() bool

type VartypeCheck

type VartypeCheck struct {
	MkLines *MkLines

	MkLine *MkLine

	// The name of the variable being checked.
	//
	// In some cases (see WithVarnameValueMatch) it contains not the
	// variable name but more a "description" of a part of a variable.
	// See MachinePlatform for an example.
	Varname    string
	Op         MkOperator
	Value      string
	ValueNoVar string
	MkComment  string // The comment including the "#".
	Guessed    bool   // Whether the type definition is guessed (based on the variable name) or explicitly defined (see vardefs.go).
}

VartypeCheck groups together the various checks for variables of the different types.

func (*VartypeCheck) Autofix

func (cv *VartypeCheck) Autofix() *Autofix

Autofix returns the autofix instance belonging to the line.

Usage:

fix := cv.Autofix()

fix.Errorf("Must not be ...")
fix.Warnf("Should not be ...")
fix.Notef("It is also possible ...")

fix.Explain(
    "Explanation ...",
    "... end of explanation.")

fix.Replace("from", "to")
fix.ReplaceAfter("prefix", "from", "to")
fix.ReplaceRegex(`[\t ]+`, "space", -1)
fix.InsertBefore("new line")
fix.InsertAfter("new line")
fix.Delete()
fix.Custom(func(showAutofix, autofix bool) {})

fix.Apply()

func (*VartypeCheck) AwkCommand

func (cv *VartypeCheck) AwkCommand()

func (*VartypeCheck) BasicRegularExpression

func (cv *VartypeCheck) BasicRegularExpression()

func (*VartypeCheck) BuildlinkDepmethod

func (cv *VartypeCheck) BuildlinkDepmethod()

func (*VartypeCheck) CFlag

func (cv *VartypeCheck) CFlag()

CFlag is a single option to the C/C++ compiler.

XXX: How can flags like "-D NAME" be handled?

func (*VartypeCheck) Category

func (cv *VartypeCheck) Category()

func (*VartypeCheck) Comment

func (cv *VartypeCheck) Comment()

Comment checks for the single-line description of a package.

The comment for categories is checked in CheckdirCategory since these almost never change.

func (*VartypeCheck) ConfFiles

func (cv *VartypeCheck) ConfFiles()

ConfFiles checks pairs of example file, configuration file.

When a package is installed, the example file is installed as usual and is then copied to its final location.

func (*VartypeCheck) Dependency

func (cv *VartypeCheck) Dependency()

func (*VartypeCheck) DependencyWithPath

func (cv *VartypeCheck) DependencyWithPath()

func (*VartypeCheck) DistSuffix

func (cv *VartypeCheck) DistSuffix()

func (*VartypeCheck) EmulPlatform

func (cv *VartypeCheck) EmulPlatform()

func (*VartypeCheck) Enum

func (cv *VartypeCheck) Enum(allowedValues map[string]bool, basicType *BasicType)

Enum checks an enumeration for valid values.

The given allowedValues contains all allowed enum values. The given basicType is only provided to lazily access the allowed enum values as a sorted list.

func (*VartypeCheck) Errorf

func (cv *VartypeCheck) Errorf(format string, args ...interface{})

func (*VartypeCheck) Explain

func (cv *VartypeCheck) Explain(explanation ...string)

func (*VartypeCheck) FetchURL

func (cv *VartypeCheck) FetchURL()

func (*VartypeCheck) FileMask

func (cv *VartypeCheck) FileMask()

func (*VartypeCheck) FileMode

func (cv *VartypeCheck) FileMode()

func (*VartypeCheck) Filename

func (cv *VartypeCheck) Filename()

Filename checks that filenames use only limited special characters.

See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_169

func (*VartypeCheck) GccReqd

func (cv *VartypeCheck) GccReqd()

func (*VartypeCheck) Homepage

func (cv *VartypeCheck) Homepage()

func (*VartypeCheck) Identifier

func (cv *VartypeCheck) Identifier()

Identifier checks for valid identifiers in various contexts, limiting the valid characters to A-Za-z0-9_.

func (*VartypeCheck) Integer

func (cv *VartypeCheck) Integer()

func (*VartypeCheck) LdFlag

func (cv *VartypeCheck) LdFlag()

func (*VartypeCheck) License

func (cv *VartypeCheck) License()

func (*VartypeCheck) MachineGnuPlatform

func (cv *VartypeCheck) MachineGnuPlatform()

func (*VartypeCheck) MachinePlatform

func (cv *VartypeCheck) MachinePlatform()

func (*VartypeCheck) MachinePlatformPattern

func (cv *VartypeCheck) MachinePlatformPattern()

func (*VartypeCheck) MailAddress

func (cv *VartypeCheck) MailAddress()

func (*VartypeCheck) Message

func (cv *VartypeCheck) Message()

Message is a plain string. It should not be enclosed in quotes since that is the job of the code that uses the message.

Lists of messages use a different type since they need the quotes around each message; see PKG_FAIL_REASON.

func (*VartypeCheck) Notef

func (cv *VartypeCheck) Notef(format string, args ...interface{})

func (*VartypeCheck) Option

func (cv *VartypeCheck) Option()

Option checks whether a single package option from options.mk conforms to the naming conventions.

func (*VartypeCheck) PathMask

func (cv *VartypeCheck) PathMask()

PathMask is a shell pattern for pathnames, possibly including slashes.

See FileMask.

func (*VartypeCheck) Pathlist

func (cv *VartypeCheck) Pathlist()

Pathlist checks variables like the PATH environment variable.

func (*VartypeCheck) Pathname

func (cv *VartypeCheck) Pathname()

Pathname checks for pathnames.

Like Filename, but including slashes.

See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_266

func (*VartypeCheck) Perl5Packlist

func (cv *VartypeCheck) Perl5Packlist()

func (*VartypeCheck) Perms

func (cv *VartypeCheck) Perms()

func (*VartypeCheck) PkgOptionsVar

func (cv *VartypeCheck) PkgOptionsVar()

func (*VartypeCheck) PkgPath

func (cv *VartypeCheck) PkgPath()

PkgPath checks a directory name relative to the top-level pkgsrc directory.

Despite its name, it is more similar to RelativePkgDir than to RelativePkgPath.

func (*VartypeCheck) PkgRevision

func (cv *VartypeCheck) PkgRevision()

func (*VartypeCheck) Pkgname

func (cv *VartypeCheck) Pkgname()

func (*VartypeCheck) PrefixPathname

func (cv *VartypeCheck) PrefixPathname()

PrefixPathname checks for a pathname relative to ${PREFIX}.

func (*VartypeCheck) PythonDependency

func (cv *VartypeCheck) PythonDependency()

func (*VartypeCheck) RPkgName

func (cv *VartypeCheck) RPkgName()

func (*VartypeCheck) RPkgVer

func (cv *VartypeCheck) RPkgVer()

func (*VartypeCheck) RelativePkgDir

func (cv *VartypeCheck) RelativePkgDir()

RelativePkgDir refers to a package directory, e.g. ../../category/pkgbase.

func (*VartypeCheck) RelativePkgPath

func (cv *VartypeCheck) RelativePkgPath()

RelativePkgPath refers to a file or directory, e.g. ../../category/pkgbase, ../../category/pkgbase/Makefile.

See RelativePkgDir, which requires a directory, not a file.

func (*VartypeCheck) Restricted

func (cv *VartypeCheck) Restricted()

func (*VartypeCheck) SedCommands

func (cv *VartypeCheck) SedCommands()

func (*VartypeCheck) ShellCommand

func (cv *VartypeCheck) ShellCommand()

func (*VartypeCheck) ShellCommands

func (cv *VartypeCheck) ShellCommands()

ShellCommands checks for zero or more shell commands, each terminated with a semicolon.

func (*VartypeCheck) ShellWord

func (cv *VartypeCheck) ShellWord()

func (*VartypeCheck) Stage

func (cv *VartypeCheck) Stage()

func (*VartypeCheck) Tool

func (cv *VartypeCheck) Tool()

Tool checks for tool names like "awk", "m4:pkgsrc", "digest:bootstrap".

TODO: Distinguish between Tool and ToolDependency.

func (*VartypeCheck) URL

func (cv *VartypeCheck) URL()

func (*VartypeCheck) Unknown

func (cv *VartypeCheck) Unknown()

Unknown doesn't check for anything.

Used for:

  • infrastructure variables that are not in vardefs.go
  • other variables whose type is unknown or uninteresting enough to warrant the creation of a specialized type

func (*VartypeCheck) UserGroupName

func (cv *VartypeCheck) UserGroupName()

func (*VartypeCheck) VariableName

func (cv *VartypeCheck) VariableName()

VariableName checks that the value is a valid variable name to be used in Makefiles.

func (*VartypeCheck) VariableNamePattern

func (cv *VartypeCheck) VariableNamePattern()

func (*VartypeCheck) Version

func (cv *VartypeCheck) Version()

func (*VartypeCheck) Warnf

func (cv *VartypeCheck) Warnf(format string, args ...interface{})

func (*VartypeCheck) WithValue

func (cv *VartypeCheck) WithValue(value string) *VartypeCheck

WithValue returns a new VartypeCheck context by copying all fields except the value.

This is typically used when calling a related check.

func (*VartypeCheck) WithVarnameValue

func (cv *VartypeCheck) WithVarnameValue(varname, value string) *VartypeCheck

WithVarnameValue returns a new VartypeCheck context by copying all fields except the variable name and the value.

This is typically used when checking parts of composite types.

func (*VartypeCheck) WithVarnameValueMatch

func (cv *VartypeCheck) WithVarnameValueMatch(varname, value string) *VartypeCheck

WithVarnameValueMatch returns a new VartypeCheck context by copying all fields except the variable name, the operator (it is set to opUseMatch) and the value.

This is typically used when checking parts of composite types, especially patterns.

func (*VartypeCheck) WrapperReorder

func (cv *VartypeCheck) WrapperReorder()

func (*VartypeCheck) WrapperTransform

func (cv *VartypeCheck) WrapperTransform()

func (*VartypeCheck) WrkdirSubdirectory

func (cv *VartypeCheck) WrkdirSubdirectory()

func (*VartypeCheck) WrksrcSubdirectory

func (cv *VartypeCheck) WrksrcSubdirectory()

WrksrcSubdirectory checks a directory relative to ${WRKSRC}, for use in CONFIGURE_DIRS and similar variables.

func (*VartypeCheck) Yes

func (cv *VartypeCheck) Yes()

func (*VartypeCheck) YesNo

func (cv *VartypeCheck) YesNo()

YesNo checks for variables that can be set to either yes or no. Undefined means no.

Most of these variables use the lowercase yes/no variant. Some use the uppercase YES/NO, and the mixed forms Yes/No are practically never seen. Testing these variables using the however-mixed pattern is done solely because writing this pattern is shorter than repeating the variable name.

func (*VartypeCheck) YesNoIndirectly

func (cv *VartypeCheck) YesNoIndirectly()

type VucQuoting

type VucQuoting uint8

VucQuoting describes in what level of quoting the variable is used. Depending on this context, the modifiers :Q or :M can be allowed or not.

The shell tokenizer knows multi-level quoting modes (see ShQuoting), but for deciding whether :Q is necessary or not, a single level is enough.

const (
	VucQuotUnknown VucQuoting = iota
	VucQuotPlain              // Example: echo LOCALBASE=${LOCALBASE}
	VucQuotDquot              // Example: echo "The version is ${PKGVERSION}."
	VucQuotSquot              // Example: echo 'The version is ${PKGVERSION}.'
	VucQuotBackt              // Example: echo `sed 1q ${WRKSRC}/README`
)

func (VucQuoting) String

func (q VucQuoting) String() string

type VucTime

type VucTime uint8

VucTime is the time at which a variable is used.

See ToolTime, which is the same except that there is no unknown.

const (
	VucUnknownTime VucTime = iota

	// VucLoadTime marks a variable use that happens directly when
	// the Makefile fragment is loaded.
	//
	// When Makefiles are loaded, the operators := and != evaluate their
	// right-hand side, as well as the directives .if, .elif and .for.
	// During loading, not all variables are available yet.
	// Variable values are still subject to change, especially lists.
	VucLoadTime

	// VucRunTime marks a variable use that happens after all files have been loaded.
	//
	// At this time, all variables can be referenced.
	//
	// At this time, variable values don't change anymore.
	// Well, except for the ::= modifier.
	// But that modifier is usually not used in pkgsrc.
	VucRunTime
)

func (VucTime) String

func (t VucTime) String() string

type YesNoUnknown

type YesNoUnknown uint8

func (YesNoUnknown) String

func (ynu YesNoUnknown) String() string

Directories

Path Synopsis
cmd
Package getopt provides a parser for command line options, supporting multi-value options such as -Wall,no-extra.
Package getopt provides a parser for command line options, supporting multi-value options such as -Wall,no-extra.
Package intqa provides quality assurance for the pkglint code.
Package intqa provides quality assurance for the pkglint code.
Package regex provides a registry of precompiled regular expressions to allow reusing them without the syntactic overhead of declaring pattern variables everywhere in the code.
Package regex provides a registry of precompiled regular expressions to allow reusing them without the syntactic overhead of declaring pattern variables everywhere in the code.
Package trace traces function calls and steps in-between.
Package trace traces function calls and steps in-between.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL