File: check_depends.go

package info (click to toggle)
dh-make-golang 0.8.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 344 kB
  • sloc: makefile: 12; sh: 9
file content (155 lines) | stat: -rw-r--r-- 3,909 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"

	"golang.org/x/mod/modfile"
	"golang.org/x/tools/go/vcs"
	"pault.ag/go/debian/control"
)

type dependency struct {
	importPath  string
	packageName string
	// todo version?
}

func execCheckDepends(args []string) {
	cwd, err := os.Getwd()
	if err != nil {
		log.Fatalf("error while getting current directory: %s", err)
	}

	// Load the already packaged Go modules
	golangBinaries, err := getGolangBinaries()
	if err != nil {
		log.Fatalf("error while getting packaged Go modules: %s", err)
	}

	// Load the dependencies defined in the Go module (go.mod)
	goModDepds, err := parseGoModDependencies(cwd, golangBinaries)
	if err != nil {
		log.Fatalf("error while parsing go.mod: %s", err)
	}

	// Load the dependencies defined in the Debian packaging (d/control)
	packageDeps, err := parseDebianControlDependencies(cwd)
	if err != nil {
		log.Fatalf("error while parsing d/control: %s", err)
	}

	hasChanged := false

	// Check for newly introduced dependencies (defined in go.mod but not in d/control)
	for _, goModDep := range goModDepds {
		found := false

		if goModDep.packageName == "" {
			fmt.Printf("NEW dependency %s is NOT yet packaged in Debian\n", goModDep.importPath)
			continue
		}

		for _, packageDep := range packageDeps {
			if packageDep.packageName == goModDep.packageName {
				found = true
				break
			}
		}

		if !found {
			hasChanged = true
			fmt.Printf("NEW dependency %s (%s)\n", goModDep.importPath, goModDep.packageName)
		}
	}

	// Check for now unused dependencies (defined in d/control but not in go.mod)
	for _, packageDep := range packageDeps {
		found := false

		for _, goModDep := range goModDepds {
			if goModDep.packageName == packageDep.packageName {
				found = true
				break
			}
		}

		if !found {
			hasChanged = true
			fmt.Printf("RM dependency %s (%s)\n", packageDep.importPath, packageDep.packageName)
		}
	}

	if !hasChanged {
		fmt.Printf("go.mod and d/control are in sync\n")
	}
}

// parseGoModDependencies parse ALL dependencies listed in go.mod
// i.e. it returns the one defined in go.mod as well as the transitively ones
// TODO: this may not be the best way of doing thing since it requires the package to be converted to go module
func parseGoModDependencies(directory string, goBinaries map[string]debianPackage) ([]dependency, error) {
	b, err := os.ReadFile(filepath.Join(directory, "go.mod"))
	if err != nil {
		return nil, err
	}

	modFile, err := modfile.Parse("go.mod", b, nil)
	if err != nil {
		return nil, err
	}

	var dependencies []dependency
	for _, require := range modFile.Require {
		if !require.Indirect {
			packageName := ""

			// Translate all packages to the root of their repository
			rr, err := vcs.RepoRootForImportPath(require.Mod.Path, false)
			if err != nil {
				log.Printf("Could not determine repo path for import path %q: %v\n", require.Mod.Path, err)
				continue
			}

			if val, exists := goBinaries[rr.Root]; exists {
				packageName = val.binary
			}

			dependencies = append(dependencies, dependency{
				importPath:  rr.Root,
				packageName: packageName,
			})
		}
	}

	return dependencies, nil
}

// parseDebianControlDependencies parse the Build-Depends defined in d/control
func parseDebianControlDependencies(directory string) ([]dependency, error) {
	ctrl, err := control.ParseControlFile(filepath.Join(directory, "debian", "control"))
	if err != nil {
		return nil, err
	}

	var dependencies []dependency

	for _, bp := range ctrl.Source.BuildDepends.GetAllPossibilities() {
		packageName := strings.Trim(bp.Name, "\n")

		// Ignore non -dev dependencies (i.e, debhelper-compat, git, cmake, etc...)
		if !strings.HasSuffix(packageName, "-dev") {
			continue
		}

		dependencies = append(dependencies, dependency{
			importPath:  "", // TODO XS-Go-Import-Path?
			packageName: packageName,
		})
	}

	return dependencies, nil
}