Skip to content

Commit

Permalink
Merge pull request #70 from trinitronx/feature/add-support-for-multip…
Browse files Browse the repository at this point in the history
…le-makefiles

Feature/add support for multiple makefiles
  • Loading branch information
mrtazz authored Nov 15, 2022
2 parents 1fa097b + e90ac15 commit 7e99987
Show file tree
Hide file tree
Showing 17 changed files with 70 additions and 19 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ not do what you want it to.
```
% checkmake Makefile
% checkmake Makefile foo.mk bar.mk baz.mk
% checkmake --help
checkmake.
Usage:
checkmake [--debug|--config=<configPath>] <makefile>
checkmake [--debug|--config=<configPath>] <makefile>...
checkmake -h | --help
checkmake --version
Expand Down
24 changes: 15 additions & 9 deletions cmd/checkmake/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var (
usage = `checkmake.
Usage:
checkmake [options] <makefile>
checkmake [options] <makefile>...
checkmake -h | --help
checkmake --version
checkmake --list-rules
Expand Down Expand Up @@ -72,13 +72,6 @@ func parseArgsAndGetFormatter(args map[string]interface{}) (formatters.Formatter
os.Exit(0)
}

makefile, parseError := parser.Parse(args["<makefile>"].(string))

if parseError != nil {
log.Fatal(parseError)
os.Exit(1)
}

if args["--config"] != nil {
configPath = args["--config"].(string)
}
Expand All @@ -90,7 +83,20 @@ func parseArgsAndGetFormatter(args map[string]interface{}) (formatters.Formatter
configPath))
}

violations := validator.Validate(makefile, cfg)
var violations rules.RuleViolationList
makefileArray := args["<makefile>"].([]string)
logger.Debug(fmt.Sprintf("Makefiles passed: %q",
makefileArray))
for _, mkf := range makefileArray {
logger.Info(fmt.Sprintf("Parsing file %q",
mkf))
makefile, parseError := parser.Parse(mkf)
if parseError != nil {
log.Fatal(parseError)
os.Exit(1)
}
violations = append(violations, validator.Validate(makefile, cfg)...)
}

var formatter formatters.Formatter

Expand Down
10 changes: 5 additions & 5 deletions formatters/custom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ import (
func TestCustomFormatter(t *testing.T) {
out := new(bytes.Buffer)

tmpl, _ := template.New("test").Parse("{{.LineNumber}}:{{.Rule}}:{{.Violation}}")
tmpl, _ := template.New("test").Parse("{{.FileName}}:{{.LineNumber}}:{{.Rule}}:{{.Violation}}")
formatter := CustomFormatter{template: tmpl, out: out}

makefile, _ := parser.Parse("../fixtures/missing_phony.make")

violations := validator.Validate(makefile, &config.Config{})
formatter.Format(violations)
assert.Regexp(t, `21:minphony:Missing required phony target "all"`, out.String())
assert.Regexp(t, `21:minphony:Missing required phony target "test"`, out.String())
assert.Regexp(t, `16:phonydeclared:Target "all" should be declared PHONY.`, out.String())
assert.Regexp(t, `../fixtures/missing_phony.make:21:minphony:Missing required phony target "all"`, out.String())
assert.Regexp(t, `../fixtures/missing_phony.make:21:minphony:Missing required phony target "test"`, out.String())
assert.Regexp(t, `../fixtures/missing_phony.make:16:phonydeclared:Target "all" should be declared PHONY.`, out.String())
}

func TestCustomFormatterNewMethod(t *testing.T) {
_, err := NewCustomFormatter("{{.LineNumber}}:{{.Rule}}:{{.Violation}}")
_, err := NewCustomFormatter("{{.FileName}}:{{.LineNumber}}:{{.Rule}}:{{.Violation}}")

assert.Equal(t, nil, err)
}
Expand Down
3 changes: 2 additions & 1 deletion formatters/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ func (f *DefaultFormatter) Format(violations rules.RuleViolationList) {
for idx, val := range violations {
data[idx] = []string{val.Rule,
val.Violation,
val.FileName,
strconv.Itoa(val.LineNumber)}
}

table := tablewriter.NewWriter(f.out)

table.SetHeader([]string{"Rule", "Description", "Line Number"})
table.SetHeader([]string{"Rule", "Description", "File Name", "Line Number"})

table.SetCenterSeparator(" ")
table.SetColumnSeparator(" ")
Expand Down
2 changes: 1 addition & 1 deletion formatters/default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestDefaultFormatter(t *testing.T) {
violations := validator.Validate(makefile, &config.Config{})
formatter.Format(violations)

assert.Regexp(t, `\s+RULE\s+DESCRIPTION\s+LINE NUMBER\s+`, out.String())
assert.Regexp(t, `\s+RULE\s+DESCRIPTION\s+FILE NAME\s+LINE NUMBER\s+`, out.String())
assert.Regexp(t, `phonydeclared\s+Target "all" should be.+\s+16`, out.String())
assert.Regexp(t, `\s+declared PHONY`, out.String())
}
4 changes: 2 additions & 2 deletions man/man1/checkmake.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ date: REPLACE_DATE

# SYNOPSIS

**checkmake** \[options\] makefile
**checkmake** \[options\] makefile ...

# DESCRIPTION
`checkmake` is an experimental linter for Makefiles. It allows for a set of
configurable rules being run against a Makefile.
configurable rules being run against a Makefile or a set of `\*.mk` files.

# OPTIONS

Expand Down
8 changes: 8 additions & 0 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

// Makefile provides a data structure to describe a parsed Makefile
type Makefile struct {
FileName string
Rules RuleList
Variables VariableList
}
Expand All @@ -24,6 +25,7 @@ type Rule struct {
Target string
Dependencies []string
Body []string
FileName string
LineNumber int
}

Expand All @@ -36,6 +38,7 @@ type Variable struct {
SimplyExpanded bool
Assignment string
SpecialVariable bool
FileName string
LineNumber int
}

Expand All @@ -56,6 +59,7 @@ var (
// know how to deal with individual lines.
func Parse(filepath string) (ret Makefile, err error) {

ret.FileName = filepath
var scanner *MakefileScanner
scanner, err = NewMakefileScanner(filepath)
if err != nil {
Expand All @@ -73,6 +77,7 @@ func Parse(filepath string) (ret Makefile, err error) {
Name: strings.TrimSpace(matches[1]),
Assignment: strings.TrimSpace(matches[2]),
SpecialVariable: true,
FileName: filepath,
LineNumber: scanner.LineNumber}
ret.Variables = append(ret.Variables, specialVar)
}
Expand Down Expand Up @@ -147,19 +152,22 @@ func parseRuleOrVariable(scanner *MakefileScanner) (ret interface{}, err error)
Target: strings.TrimSpace(matches[1]),
Dependencies: filteredDeps,
Body: ruleBody,
FileName: scanner.FileHandle.Name(),
LineNumber: beginLineNumber}
} else if matches := reFindSimpleVariable.FindStringSubmatch(line); matches != nil {
ret = Variable{
Name: strings.TrimSpace(matches[1]),
Assignment: strings.TrimSpace(matches[2]),
SimplyExpanded: true,
FileName: scanner.FileHandle.Name(),
LineNumber: scanner.LineNumber}
scanner.Scan()
} else if matches := reFindExpandedVariable.FindStringSubmatch(line); matches != nil {
ret = Variable{
Name: strings.TrimSpace(matches[1]),
Assignment: strings.TrimSpace(matches[2]),
SimplyExpanded: false,
FileName: scanner.FileHandle.Name(),
LineNumber: scanner.LineNumber}
scanner.Scan()
} else {
Expand Down
9 changes: 9 additions & 0 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,47 @@ func TestParseSimpleMakefile(t *testing.T) {
ret, err := Parse("../fixtures/simple.make")

assert.Equal(t, err, nil)
assert.Equal(t, ret.FileName, "../fixtures/simple.make")
assert.Equal(t, len(ret.Rules), 5)
assert.Equal(t, len(ret.Variables), 4)
assert.Equal(t, ret.Rules[0].Target, "clean")
assert.Equal(t, ret.Rules[0].Body, []string{"rm bar", "rm foo"})
assert.Equal(t, ret.Rules[0].FileName, "../fixtures/simple.make")

assert.Equal(t, ret.Rules[1].Target, "foo")
assert.Equal(t, ret.Rules[1].Body, []string{"touch foo"})
assert.Equal(t, ret.Rules[1].Dependencies, []string{"bar"})
assert.Equal(t, ret.Rules[1].FileName, "../fixtures/simple.make")

assert.Equal(t, ret.Rules[2].Target, "bar")
assert.Equal(t, ret.Rules[2].Body, []string{"touch bar"})
assert.Equal(t, ret.Rules[2].FileName, "../fixtures/simple.make")

assert.Equal(t, ret.Rules[3].Target, "all")
assert.Equal(t, ret.Rules[3].Dependencies, []string{"foo"})
assert.Equal(t, ret.Rules[3].FileName, "../fixtures/simple.make")

assert.Equal(t, ret.Variables[0].Name, "expanded")
assert.Equal(t, ret.Variables[0].Assignment, "\"$(simple)\"")
assert.Equal(t, ret.Variables[0].SimplyExpanded, false)
assert.Equal(t, ret.Variables[0].SpecialVariable, false)
assert.Equal(t, ret.Variables[0].FileName, "../fixtures/simple.make")

assert.Equal(t, ret.Variables[1].Name, "simple")
assert.Equal(t, ret.Variables[1].Assignment, "\"foo\"")
assert.Equal(t, ret.Variables[1].SimplyExpanded, true)
assert.Equal(t, ret.Variables[1].SpecialVariable, false)
assert.Equal(t, ret.Variables[1].FileName, "../fixtures/simple.make")

assert.Equal(t, ret.Variables[2].Name, "PHONY")
assert.Equal(t, ret.Variables[2].Assignment, "all clean test")
assert.Equal(t, ret.Variables[2].SimplyExpanded, false)
assert.Equal(t, ret.Variables[2].SpecialVariable, true)
assert.Equal(t, ret.Variables[2].FileName, "../fixtures/simple.make")

assert.Equal(t, ret.Variables[3].Name, "DEFAULT_GOAL")
assert.Equal(t, ret.Variables[3].Assignment, "all")
assert.Equal(t, ret.Variables[3].SimplyExpanded, false)
assert.Equal(t, ret.Variables[3].SpecialVariable, true)
assert.Equal(t, ret.Variables[3].FileName, "../fixtures/simple.make")
}
1 change: 1 addition & 0 deletions rules/maxbodylength/maxbodylength.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func (m *MaxBodyLength) Run(makefile parser.Makefile, config rules.RuleConfig) r
ret = append(ret, rules.RuleViolation{
Rule: "maxbodylength",
Violation: fmt.Sprintf(vT, rule.Target, maxBodyLength, len(rule.Body)),
FileName: makefile.FileName,
LineNumber: rule.LineNumber,
})
}
Expand Down
4 changes: 4 additions & 0 deletions rules/maxbodylength/maxbodylength_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
func TestFooIsTooLong(t *testing.T) {

makefile := parser.Makefile{
FileName: "maxbodylength.mk",
Rules: []parser.Rule{parser.Rule{
Target: "foo",
Body: []string{"echo 'foo'",
Expand All @@ -32,11 +33,13 @@ func TestFooIsTooLong(t *testing.T) {
rule.Description())
assert.Equal(t, "Target body for \"foo\" exceeds allowed length of 5 (7).", ret[0].Violation)
assert.Equal(t, 1, ret[0].LineNumber)
assert.Equal(t, "maxbodylength.mk", ret[0].FileName)
}

func TestFooIsTooLongWithConfig(t *testing.T) {

makefile := parser.Makefile{
FileName: "maxbodylength.mk",
Rules: []parser.Rule{parser.Rule{
Target: "foo",
Body: []string{"echo 'foo'",
Expand All @@ -58,4 +61,5 @@ func TestFooIsTooLongWithConfig(t *testing.T) {
rule.Description())
assert.Equal(t, "Target body for \"foo\" exceeds allowed length of 3 (4).", ret[0].Violation)
assert.Equal(t, 1, ret[0].LineNumber)
assert.Equal(t, "maxbodylength.mk", ret[0].FileName)
}
1 change: 1 addition & 0 deletions rules/minphony/minphony.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ func (r *MinPhony) Run(makefile parser.Makefile, _ rules.RuleConfig) rules.RuleV
ret = append(ret, rules.RuleViolation{
Rule: "minphony",
Violation: fmt.Sprintf("Missing required phony target %q", reqRule),
FileName: makefile.FileName,
LineNumber: ruleLineNumber,
})
}
Expand Down
6 changes: 6 additions & 0 deletions rules/minphony/minphony_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var mpRunTests = []struct {
}{
{
mf: parser.Makefile{
FileName: "green-eggs.mk",
Rules: parser.RuleList{
{Target: "green-eggs"},
{Target: "ham"},
Expand All @@ -26,22 +27,26 @@ var mpRunTests = []struct {
rules.RuleViolation{
Rule: "minphony",
Violation: "Missing required phony target \"kleen\"",
FileName: "green-eggs.mk",
LineNumber: -1,
},
rules.RuleViolation{
Rule: "minphony",
Violation: "Missing required phony target \"awl\"",
FileName: "green-eggs.mk",
LineNumber: -1,
},
rules.RuleViolation{
Rule: "minphony",
Violation: "Missing required phony target \"toast\"",
FileName: "green-eggs.mk",
LineNumber: -1,
},
},
},
{
mf: parser.Makefile{
FileName: "kleen.mk",
Rules: parser.RuleList{
{Target: "awl"},
{Target: "distkleen"},
Expand All @@ -55,6 +60,7 @@ var mpRunTests = []struct {
rules.RuleViolation{
Rule: "minphony",
Violation: "Missing required phony target \"toast\"",
FileName: "kleen.mk",
LineNumber: -1,
},
},
Expand Down
1 change: 1 addition & 0 deletions rules/phonydeclared/phonydeclared.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func (r *Phonydeclared) Run(makefile parser.Makefile, config rules.RuleConfig) r
ret = append(ret, rules.RuleViolation{
Rule: "phonydeclared",
Violation: fmt.Sprintf("Target %q should be declared PHONY.", rule.Target),
FileName: makefile.FileName,
LineNumber: rule.LineNumber,
})
}
Expand Down
5 changes: 5 additions & 0 deletions rules/phonydeclared/phonydeclared_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
func TestAllTargetsArePhony(t *testing.T) {

makefile := parser.Makefile{
FileName: "phony-declared-all-phony.mk",
Variables: []parser.Variable{parser.Variable{
Name: "PHONY",
Assignment: "all clean"}},
Expand All @@ -29,6 +30,7 @@ func TestAllTargetsArePhony(t *testing.T) {
func TestMissingOnePhonyTarget(t *testing.T) {

makefile := parser.Makefile{
FileName: "phony-declared-missing-one-phony.mk",
Variables: []parser.Variable{parser.Variable{
Name: "PHONY",
Assignment: "all"}},
Expand All @@ -42,4 +44,7 @@ func TestMissingOnePhonyTarget(t *testing.T) {

assert.Equal(t, len(ret), 1)

for i := range ret {
assert.Equal(t, "phony-declared-missing-one-phony.mk", ret[i].FileName)
}
}
1 change: 1 addition & 0 deletions rules/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Rule interface {
type RuleViolation struct {
Rule string
Violation string
FileName string
LineNumber int
}

Expand Down
1 change: 1 addition & 0 deletions rules/timestampexpanded/timestampexpanded.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func (r *Timestampexpanded) Run(makefile parser.Makefile, config rules.RuleConfi
ret = append(ret, rules.RuleViolation{
Rule: "timestampexpanded",
Violation: fmt.Sprintf(vT, variable.Name),
FileName: makefile.FileName,
LineNumber: variable.LineNumber,
})
}
Expand Down
5 changes: 5 additions & 0 deletions rules/timestampexpanded/timestampexpanded_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
func TestVersionIsNotSimplyExpanded(t *testing.T) {

makefile := parser.Makefile{
FileName: "timestamp-expanded.mk",
Variables: []parser.Variable{parser.Variable{
Name: "BUILDTIME",
Assignment: "$(shell date -u +\"%Y-%m-%dT%H:%M:%SZ\")",
Expand All @@ -24,11 +25,15 @@ func TestVersionIsNotSimplyExpanded(t *testing.T) {
assert.Equal(t, 1, len(ret))
assert.Equal(t, "timestamp variables should be simply expanded",
rule.Description())
for i := range ret {
assert.Equal(t, "timestamp-expanded.mk", ret[i].FileName)
}
}

func TestVersionIsSimplyExpanded(t *testing.T) {

makefile := parser.Makefile{
FileName: "timestamp-simply-expanded.mk",
Variables: []parser.Variable{parser.Variable{
Name: "BUILDTIME",
Assignment: "$(shell date -u +\"%Y-%m-%dT%H:%M:%SZ\")",
Expand Down

0 comments on commit 7e99987

Please sign in to comment.