Annotation of pkgsrc/pkgtools/pkglint/files/pkgsrc.go, Revision 1.5
1.1 rillig 1: package main
2:
3: import (
4: "io/ioutil"
5: "netbsd.org/pkglint/regex"
6: "netbsd.org/pkglint/trace"
7: "sort"
8: "strings"
9: )
10:
11: // Pkgsrc describes a pkgsrc installation.
12: // In each pkglint run, only a single pkgsrc installation is ever loaded.
13: // It just doesn't make sense to check multiple pkgsrc installations at once.
1.3 rillig 14: type Pkgsrc struct {
1.1 rillig 15: // The top directory (PKGSRCDIR), either absolute or relative to
16: // the current working directory.
17: topdir string
18:
19: // The set of user-defined variables that are added to BUILD_DEFS
20: // within the bsd.pkg.mk file.
21: buildDefs map[string]bool
22:
23: Tools ToolRegistry
24:
25: MasterSiteURLToVar map[string]string // "https://github.com/" => "MASTER_SITE_GITHUB"
26: MasterSiteVarToURL map[string]string // "MASTER_SITE_GITHUB" => "https://github.com/"
27:
28: PkgOptions map[string]string // "x11" => "Provides X11 support"
29:
30: suggestedUpdates []SuggestedUpdate //
31: suggestedWipUpdates []SuggestedUpdate //
32: LastChange map[string]*Change //
33: latest map[string]string // "lang/php[0-9]*" => "lang/php70"
34:
35: UserDefinedVars map[string]MkLine // varname => line; used for checking BUILD_DEFS
36: Deprecated map[string]string //
37: vartypes map[string]*Vartype // varcanon => type
1.4 rillig 38:
39: Hashes map[string]*Hash // Maps "alg:fname" => hash (inter-package check).
40: UsedLicenses map[string]bool // Maps "license name" => true (inter-package check).
1.1 rillig 41: }
42:
1.3 rillig 43: func NewPkgsrc(dir string) *Pkgsrc {
44: src := &Pkgsrc{
1.1 rillig 45: dir,
46: make(map[string]bool),
47: NewToolRegistry(),
48: make(map[string]string),
49: make(map[string]string),
50: make(map[string]string),
51: nil,
52: nil,
53: make(map[string]*Change),
54: make(map[string]string),
55: make(map[string]MkLine),
56: make(map[string]string),
1.4 rillig 57: make(map[string]*Vartype),
58: nil, // Only initialized when pkglint is run for a whole pkgsrc installation
59: nil}
1.1 rillig 60:
61: // Some user-defined variables do not influence the binary
62: // package at all and therefore do not have to be added to
63: // BUILD_DEFS; therefore they are marked as "already added".
64: src.AddBuildDef("DISTDIR")
65: src.AddBuildDef("FETCH_CMD")
66: src.AddBuildDef("FETCH_OUTPUT_ARGS")
67:
68: // The following variables are not expected to be modified
69: // by the pkgsrc user. They are added here to prevent unnecessary
70: // warnings by pkglint.
71: src.AddBuildDef("GAMES_USER")
72: src.AddBuildDef("GAMES_GROUP")
73: src.AddBuildDef("GAMEDATAMODE")
74: src.AddBuildDef("GAMEDIRMODE")
75: src.AddBuildDef("GAMEMODE")
76: src.AddBuildDef("GAMEOWN")
77: src.AddBuildDef("GAMEGRP")
78:
79: return src
80: }
81:
82: // Load reads the pkgsrc infrastructure files to
83: // extract information like the tools, packages to update,
84: // user-defined variables.
85: //
86: // This work is not done in the constructor to keep the tests
87: // simple, since setting up a realistic pkgsrc environment requires
88: // a lot of files.
1.3 rillig 89: func (src *Pkgsrc) Load() {
1.1 rillig 90: src.InitVartypes()
91: src.loadMasterSites()
92: src.loadPkgOptions()
93: src.loadDocChanges()
94: src.loadSuggestedUpdates()
95: src.loadUserDefinedVars()
96: src.loadTools()
97: src.initDeprecatedVars()
98: }
99:
100: // Latest returns the latest package matching the given pattern.
101: // It searches the `category` for subdirectories matching the given
102: // regular expression, and returns the `repl` string, in which the
103: // placeholder is filled with the best result.
104: //
105: // Example:
106: // Latest("lang", `^php[0-9]+$`, "../../lang/$0") => "../../lang/php72"
1.3 rillig 107: func (src *Pkgsrc) Latest(category string, re regex.Pattern, repl string) string {
1.1 rillig 108: key := category + "/" + string(re) + " => " + repl
109: if latest, found := src.latest[key]; found {
110: return latest
111: }
112:
113: if src.latest == nil {
114: src.latest = make(map[string]string)
115: }
116:
117: categoryDir := src.File(category)
118: error := func() string {
119: dummyLine.Errorf("Cannot find latest version of %q in %q.", re, categoryDir)
120: src.latest[key] = ""
121: return ""
122: }
123:
124: all, err := ioutil.ReadDir(categoryDir)
125: sort.SliceStable(all, func(i, j int) bool {
126: return naturalLess(all[i].Name(), all[j].Name())
127: })
128: if err != nil {
129: return error()
130: }
131:
132: latest := ""
133: for _, fileInfo := range all {
134: if matches(fileInfo.Name(), re) {
135: latest = regex.Compile(re).ReplaceAllString(fileInfo.Name(), repl)
136: }
137: }
138: if latest == "" {
139: return error()
140: }
141:
142: src.latest[key] = latest
143: return latest
144: }
145:
146: // loadTools loads the tool definitions from `mk/tools/*`.
1.3 rillig 147: func (src *Pkgsrc) loadTools() {
1.1 rillig 148: toolFiles := []string{"defaults.mk"}
149: {
1.2 rillig 150: toc := G.Pkgsrc.File("mk/tools/bsd.tools.mk")
151: lines := LoadExistingLines(toc, true)
1.1 rillig 152: for _, line := range lines {
153: if m, _, _, includefile := MatchMkInclude(line.Text); m {
154: if !contains(includefile, "/") {
155: toolFiles = append(toolFiles, includefile)
156: }
157: }
158: }
159: if len(toolFiles) <= 1 {
1.2 rillig 160: NewLine(toc, 0, "", nil).Fatalf("Too few tool files.")
1.1 rillig 161: }
162: }
163:
164: reg := src.Tools
1.2 rillig 165: reg.RegisterTool(&Tool{"echo", "ECHO", true, true, true}, dummyLine)
166: reg.RegisterTool(&Tool{"echo -n", "ECHO_N", true, true, true}, dummyLine)
167: reg.RegisterTool(&Tool{"false", "FALSE", true /*why?*/, true, false}, dummyLine)
168: reg.RegisterTool(&Tool{"test", "TEST", true, true, true}, dummyLine)
169: reg.RegisterTool(&Tool{"true", "TRUE", true /*why?*/, true, true}, dummyLine)
1.1 rillig 170:
171: for _, basename := range toolFiles {
172: lines := G.Pkgsrc.LoadExistingLines("mk/tools/"+basename, true)
173: for _, line := range lines {
174: reg.ParseToolLine(line)
175: }
176: }
177:
178: for _, relativeName := range [...]string{"mk/bsd.prefs.mk", "mk/bsd.pkg.mk"} {
179: condDepth := 0
180:
181: lines := G.Pkgsrc.LoadExistingLines(relativeName, true)
182: for _, line := range lines {
183: text := line.Text
184: if hasPrefix(text, "#") {
185: continue
186: }
187:
188: if m, _, varname, _, _, _, value, _, _ := MatchVarassign(text); m {
189: if varname == "USE_TOOLS" {
190: if trace.Tracing {
191: trace.Stepf("[condDepth=%d] %s", condDepth, value)
192: }
193: if condDepth == 0 || condDepth == 1 && relativeName == "mk/bsd.prefs.mk" {
194: for _, toolname := range splitOnSpace(value) {
195: if !containsVarRef(toolname) {
1.2 rillig 196: tool := reg.Register(toolname, line)
197: tool.Predefined = true
198: if relativeName == "mk/bsd.prefs.mk" {
199: tool.UsableAtLoadtime = true
1.1 rillig 200: }
201: }
202: }
203: }
204:
205: } else if varname == "_BUILD_DEFS" {
206: for _, bdvar := range splitOnSpace(value) {
207: src.AddBuildDef(bdvar)
208: }
209: }
210:
1.5 ! rillig 211: } else if m, _, cond, _, _ := matchMkCond(text); m {
1.1 rillig 212: switch cond {
213: case "if", "ifdef", "ifndef", "for":
214: condDepth++
215: case "endif", "endfor":
216: condDepth--
217: }
218: }
219: }
220: }
221:
222: if trace.Tracing {
223: reg.Trace()
224: }
225: }
226:
1.3 rillig 227: func (src *Pkgsrc) loadSuggestedUpdatesFile(fname string) []SuggestedUpdate {
1.1 rillig 228: lines := LoadExistingLines(fname, false)
229: return src.parseSuggestedUpdates(lines)
230: }
231:
1.3 rillig 232: func (src *Pkgsrc) parseSuggestedUpdates(lines []Line) []SuggestedUpdate {
1.1 rillig 233: var updates []SuggestedUpdate
234: state := 0
235: for _, line := range lines {
236: text := line.Text
237:
238: if state == 0 && text == "Suggested package updates" {
239: state = 1
240: } else if state == 1 && text == "" {
241: state = 2
242: } else if state == 2 {
243: state = 3
244: } else if state == 3 && text == "" {
245: state = 4
246: }
247:
248: if state == 3 {
249: if m, pkgname, comment := match2(text, `^\to\s(\S+)(?:\s*(.+))?$`); m {
250: if m, pkgbase, pkgversion := match2(pkgname, rePkgname); m {
251: updates = append(updates, SuggestedUpdate{line, pkgbase, pkgversion, comment})
252: } else {
253: line.Warnf("Invalid package name %q", pkgname)
254: }
255: } else {
256: line.Warnf("Invalid line format %q", text)
257: }
258: }
259: }
260: return updates
261: }
262:
1.3 rillig 263: func (src *Pkgsrc) loadSuggestedUpdates() {
1.1 rillig 264: src.suggestedUpdates = src.loadSuggestedUpdatesFile(G.Pkgsrc.File("doc/TODO"))
265: if wipFilename := G.Pkgsrc.File("wip/TODO"); fileExists(wipFilename) {
266: src.suggestedWipUpdates = src.loadSuggestedUpdatesFile(wipFilename)
267: }
268: }
269:
1.3 rillig 270: func (src *Pkgsrc) loadDocChangesFromFile(fname string) []*Change {
1.1 rillig 271: lines := LoadExistingLines(fname, false)
272:
273: parseChange := func(line Line) *Change {
274: text := line.Text
275: if !hasPrefix(text, "\t") {
276: return nil
277: }
278:
279: f := strings.Fields(text)
280: n := len(f)
281: if n != 4 && n != 6 {
282: return nil
283: }
284:
285: action, pkgpath, author, date := f[0], f[1], f[len(f)-2], f[len(f)-1]
286: if !hasPrefix(author, "[") || !hasSuffix(date, "]") {
287: return nil
288: }
289: author, date = author[1:], date[:len(date)-1]
290:
291: switch {
292: case action == "Added" && f[2] == "version" && n == 6:
293: return &Change{line, action, pkgpath, f[3], author, date}
294: case (action == "Updated" || action == "Downgraded") && f[2] == "to" && n == 6:
295: return &Change{line, action, pkgpath, f[3], author, date}
296: case action == "Removed" && (n == 6 && f[2] == "successor" || n == 4):
297: return &Change{line, action, pkgpath, "", author, date}
298: case (action == "Renamed" || action == "Moved") && f[2] == "to" && n == 6:
299: return &Change{line, action, pkgpath, "", author, date}
300: }
301: return nil
302: }
303:
304: var changes []*Change
305: for _, line := range lines {
306: if change := parseChange(line); change != nil {
307: changes = append(changes, change)
308: } else if text := line.Text; len(text) >= 2 && text[0] == '\t' && 'A' <= text[1] && text[1] <= 'Z' {
309: line.Warnf("Unknown doc/CHANGES line: %q", text)
310: Explain("See mk/misc/developer.mk for the rules.")
311: }
312: }
313: return changes
314: }
315:
1.3 rillig 316: func (src *Pkgsrc) GetSuggestedPackageUpdates() []SuggestedUpdate {
1.1 rillig 317: if G.Wip {
318: return src.suggestedWipUpdates
319: } else {
320: return src.suggestedUpdates
321: }
322: }
323:
1.3 rillig 324: func (src *Pkgsrc) loadDocChanges() {
1.1 rillig 325: docdir := G.Pkgsrc.File("doc")
326: files, err := ioutil.ReadDir(docdir)
327: if err != nil {
328: NewLineWhole(docdir).Fatalf("Cannot be read.")
329: }
330:
331: var fnames []string
332: for _, file := range files {
333: fname := file.Name()
334: if matches(fname, `^CHANGES-20\d\d$`) && fname >= "CHANGES-2011" {
335: fnames = append(fnames, fname)
336: }
337: }
338:
339: sort.Strings(fnames)
340: src.LastChange = make(map[string]*Change)
341: for _, fname := range fnames {
342: changes := src.loadDocChangesFromFile(docdir + "/" + fname)
343: for _, change := range changes {
344: src.LastChange[change.Pkgpath] = change
345: }
346: }
347: }
348:
1.3 rillig 349: func (src *Pkgsrc) loadUserDefinedVars() {
1.1 rillig 350: lines := G.Pkgsrc.LoadExistingLines("mk/defaults/mk.conf", true)
351: mklines := NewMkLines(lines)
352:
353: for _, mkline := range mklines.mklines {
354: if mkline.IsVarassign() {
355: src.UserDefinedVars[mkline.Varname()] = mkline
356: }
357: }
358: }
359:
1.3 rillig 360: func (src *Pkgsrc) initDeprecatedVars() {
1.1 rillig 361: src.Deprecated = map[string]string{
362: // December 2003
363: "FIX_RPATH": "It has been removed from pkgsrc in 2003.",
364:
365: // February 2005
366: "LIB_DEPENDS": "Use DEPENDS instead.",
367: "ONLY_FOR_ARCHS": "Use ONLY_FOR_PLATFORM instead.",
368: "NOT_FOR_ARCHS": "Use NOT_FOR_PLATFORM instead.",
369: "ONLY_FOR_OPSYS": "Use ONLY_FOR_PLATFORM instead.",
370: "NOT_FOR_OPSYS": "Use NOT_FOR_PLATFORM instead.",
371:
372: // May 2005
373: "ALL_TARGET": "Use BUILD_TARGET instead.",
374: "DIGEST_FILE": "Use DISTINFO_FILE instead.",
375: "IGNORE": "Use PKG_FAIL_REASON or PKG_SKIP_REASON instead.",
376: "IS_INTERACTIVE": "Use INTERACTIVE_STAGE instead.",
377: "KERBEROS": "Use the PKG_OPTIONS framework instead.",
378: "MASTER_SITE_SUBDIR": "Use some form of MASTER_SITES instead.",
379: "MD5_FILE": "Use DISTINFO_FILE instead.",
380: "MIRROR_DISTFILE": "Use NO_BIN_ON_FTP and/or NO_SRC_ON_FTP instead.",
381: "NO_CDROM": "Use NO_BIN_ON_CDROM and/or NO_SRC_ON_CDROM instead.",
382: "NO_PATCH": "You can just remove it.",
383: "NO_WRKSUBDIR": "Use WRKSRC=${WRKDIR} instead.",
384: "PATCH_SITE_SUBDIR": "Use some form of PATCHES_SITES instead.",
385: "PATCH_SUM_FILE": "Use DISTINFO_FILE instead.",
386: "PKG_JVM": "Use PKG_DEFAULT_JVM instead.",
387: "USE_BUILDLINK2": "You can just remove it.",
388: "USE_BUILDLINK3": "You can just remove it.",
389: "USE_CANNA": "Use the PKG_OPTIONS framework instead.",
390: "USE_DB4": "Use the PKG_OPTIONS framework instead.",
391: "USE_DIRS": "You can just remove it.",
392: "USE_ESOUND": "Use the PKG_OPTIONS framework instead.",
393: "USE_GIF": "Use the PKG_OPTIONS framework instead.",
394: "USE_GMAKE": "Use USE_TOOLS+=gmake instead.",
395: "USE_GNU_TOOLS": "Use USE_TOOLS instead.",
396: "USE_IDEA": "Use the PKG_OPTIONS framework instead.",
397: "USE_LIBCRACK": "Use the PKG_OPTIONS framework instead.",
398: "USE_MMX": "Use the PKG_OPTIONS framework instead.",
399: "USE_PKGLIBTOOL": "Use USE_LIBTOOL instead.",
400: "USE_SSL": "Include \"../../security/openssl/buildlink3.mk\" instead.",
401:
402: // July 2005
403: "USE_PERL5": "Use USE_TOOLS+=perl or USE_TOOLS+=perl:run instead.",
404:
405: // October 2005
406: "NO_TOOLS": "You can just remove it.",
407: "NO_WRAPPER": "You can just remove it.",
408:
409: // November 2005
410: "ALLFILES": "Use CKSUMFILES instead.",
411: "DEPENDS_TARGET": "Use DEPENDS instead.",
412: "FETCH_DEPENDS": "Use DEPENDS instead.",
413: "RUN_DEPENDS": "Use DEPENDS instead.",
414:
415: // December 2005
416: "USE_CUPS": "Use the PKG_OPTIONS framework (option cups) instead.",
417: "USE_I586": "Use the PKG_OPTIONS framework (option i586) instead.",
418: "USE_INN": "Use the PKG_OPTIONS framework instead.",
419: "USE_OPENLDAP": "Use the PKG_OPTIONS framework (option openldap) instead.",
420: "USE_OSS": "Use the PKG_OPTIONS framework (option oss) instead.",
421: "USE_RSAREF2": "Use the PKG_OPTIONS framework (option rsaref) instead.",
422: "USE_SASL": "Use the PKG_OPTIONS framework (option sasl) instead.",
423: "USE_SASL2": "Use the PKG_OPTIONS framework (option sasl) instead.",
424: "USE_SJ3": "Use the PKG_OPTIONS framework (option sj3) instead.",
425: "USE_SOCKS": "Use the PKG_OPTIONS framework (socks4 and socks5 options) instead.",
426: "USE_WNN4": "Use the PKG_OPTIONS framework (option wnn4) instead.",
427: "USE_XFACE": "Use the PKG_OPTIONS framework instead.",
428:
429: // February 2006
430: "TOOLS_DEPMETHOD": "Use the :build or :run modifiers in USE_TOOLS instead.",
431: "MANDIR": "Please use ${PREFIX}/${PKGMANDIR} instead.",
432: "DOWNLOADED_DISTFILE": "Use the shell variable $$extract_file instead.",
433: "DECOMPRESS_CMD": "Use EXTRACT_CMD instead.",
434:
435: // March 2006
436: "INSTALL_EXTRA_TMPL": "Use INSTALL_TEMPLATE instead.",
437: "DEINSTALL_EXTRA_TMPL": "Use DEINSTALL_TEMPLATE instead.",
438:
439: // April 2006
440: "RECOMMENDED": "Use ABI_DEPENDS instead.",
441: "BUILD_USES_MSGFMT": "Use USE_TOOLS+=msgfmt instead.",
442: "USE_MSGFMT_PLURALS": "Use USE_TOOLS+=msgfmt instead.",
443:
444: // May 2006
445: "EXTRACT_USING_PAX": "Use \"EXTRACT_OPTS=-t pax\" instead.",
446: "NO_EXTRACT": "It doesn't exist anymore.",
447: "_FETCH_MESSAGE": "Use FETCH_MESSAGE (different format) instead.",
448: "BUILDLINK_DEPENDS.*": "Use BUILDLINK_API_DEPENDS.* instead.",
449: "BUILDLINK_RECOMMENDED.*": "Use BUILDLINK_ABI_DEPENDS.* instead.",
450: "SHLIB_HANDLING": "Use CHECK_SHLIBS_SUPPORTED instead.",
451: "USE_RMAN": "It has been removed.",
452:
453: // June 2006
454: "DEINSTALL_SRC": "Use the pkginstall framework instead.",
455: "INSTALL_SRC": "Use the pkginstall framework instead.",
456: "DEINSTALL_TEMPLATE": "Use DEINSTALL_TEMPLATES instead.",
457: "INSTALL_TEMPLATE": "Use INSTALL_TEMPLATES instead.",
458: "HEADER_TEMPLATE": "Use HEADER_TEMPLATES instead.",
459: "_REPLACE.*": "Use REPLACE.* instead.",
460: "_REPLACE_FILES.*": "Use REPLACE_FILES.* instead.",
461: "MESSAGE": "Use MESSAGE_SRC instead.",
462: "INSTALL_FILE": "It may only be used internally by pkgsrc.",
463: "DEINSTALL_FILE": "It may only be used internally by pkgsrc.",
464:
465: // July 2006
466: "USE_DIGEST": "You can just remove it.",
467: "LTCONFIG_OVERRIDE": "You can just remove it.",
468: "USE_GNU_GETTEXT": "You can just remove it.",
469: "BUILD_ENV": "Use PKGSRC_MAKE_ENV instead.",
470: "DYNAMIC_MASTER_SITES": "You can just remove it.",
471:
472: // September 2006
473: "MAKEFILE": "Use MAKE_FILE instead.",
474:
475: // November 2006
476: "SKIP_PORTABILITY_CHECK": "Use CHECK_PORTABILITY_SKIP (a list of patterns) instead.",
477:
478: // January 2007
479: "BUILDLINK_TRANSFORM.*": "Use BUILDLINK_FNAME_TRANSFORM.* instead.",
480:
481: // March 2007
482: "SCRIPTDIR": "You can just remove it.",
483: "NO_PKG_REGISTER": "You can just remove it.",
484: "NO_DEPENDS": "You can just remove it.",
485:
486: // October 2007
487: "_PKG_SILENT": "Use RUN (with more error checking) instead.",
488: "_PKG_DEBUG": "Use RUN (with more error checking) instead.",
489: "LICENCE": "Use LICENSE instead.",
490:
491: // November 2007
492: //USE_NCURSES Include "../../devel/ncurses/buildlink3.mk" instead.
493:
494: // December 2007
495: "INSTALLATION_DIRS_FROM_PLIST": "Use AUTO_MKDIRS instead.",
496:
497: // April 2009
498: "NO_PACKAGE": "It doesn't exist anymore.",
499: "NO_MTREE": "You can just remove it.",
500:
501: // July 2012
502: "SETGIDGAME": "Use USE_GAMESGROUP instead.",
503: "GAMEGRP": "Use GAMES_GROUP instead.",
504: "GAMEOWN": "Use GAMES_USER instead.",
505:
506: // July 2013
507: "USE_GNU_READLINE": "Include \"../../devel/readline/buildlink3.mk\" instead.",
508:
509: // October 2014
510: "SVR4_PKGNAME": "Just remove it.",
511: "PKG_INSTALLATION_TYPES": "Just remove it.",
512:
513: // January 2016
514: "SUBST_POSTCMD.*": "Has been removed, as it seemed unused.",
515:
516: // June 2016
517: "USE_CROSSBASE": "Has been removed.",
518: }
519: }
520:
521: // LoadExistingLines loads the file relative to the pkgsrc top directory.
1.3 rillig 522: func (src *Pkgsrc) LoadExistingLines(fileName string, joinBackslashLines bool) []Line {
1.2 rillig 523: return LoadExistingLines(src.File(fileName), joinBackslashLines)
1.1 rillig 524: }
525:
526: // File resolves a file name relative to the pkgsrc top directory.
527: //
528: // Example:
529: // NewPkgsrc("/usr/pkgsrc").File("distfiles") => "/usr/pkgsrc/distfiles"
1.3 rillig 530: func (src *Pkgsrc) File(relativeName string) string {
1.1 rillig 531: return src.topdir + "/" + relativeName
532: }
533:
534: // ToRel returns the path of `fileName`, relative to the pkgsrc top directory.
535: //
536: // Example:
537: // NewPkgsrc("/usr/pkgsrc").ToRel("/usr/pkgsrc/distfiles") => "distfiles"
1.3 rillig 538: func (src *Pkgsrc) ToRel(fileName string) string {
1.1 rillig 539: return relpath(src.topdir, fileName)
540: }
541:
1.3 rillig 542: func (src *Pkgsrc) AddBuildDef(varname string) {
1.1 rillig 543: src.buildDefs[varname] = true
544: }
545:
1.3 rillig 546: func (src *Pkgsrc) IsBuildDef(varname string) bool {
1.1 rillig 547: return src.buildDefs[varname]
548: }
549:
1.3 rillig 550: func (src *Pkgsrc) loadMasterSites() {
1.1 rillig 551: lines := src.LoadExistingLines("mk/fetch/sites.mk", true)
552:
553: nameToUrl := src.MasterSiteVarToURL
554: urlToName := src.MasterSiteURLToVar
555: for _, line := range lines {
556: if m, commented, varname, _, _, _, urls, _, _ := MatchVarassign(line.Text); m {
557: if !commented && hasPrefix(varname, "MASTER_SITE_") && varname != "MASTER_SITE_BACKUP" {
558: for _, url := range splitOnSpace(urls) {
559: if matches(url, `^(?:http://|https://|ftp://)`) {
560: if nameToUrl[varname] == "" {
561: nameToUrl[varname] = url
562: }
563: urlToName[url] = varname
564: }
565: }
566: }
567: }
568: }
569:
570: // Explicitly allowed, although not defined in mk/fetch/sites.mk.
571: nameToUrl["MASTER_SITE_LOCAL"] = "ftp://ftp.NetBSD.org/pub/pkgsrc/distfiles/LOCAL_PORTS/"
572:
573: if trace.Tracing {
574: trace.Stepf("Loaded %d MASTER_SITE_* URLs.", len(urlToName))
575: }
576: }
577:
1.3 rillig 578: func (src *Pkgsrc) loadPkgOptions() {
1.1 rillig 579: lines := src.LoadExistingLines("mk/defaults/options.description", false)
580:
581: for _, line := range lines {
582: if m, optname, optdescr := match2(line.Text, `^([-0-9a-z_+]+)(?:\s+(.*))?$`); m {
583: src.PkgOptions[optname] = optdescr
584: } else {
585: line.Fatalf("Unknown line format.")
586: }
587: }
588: }
589:
590: // Change is a change entry from the `doc/CHANGES-*` files.
591: type Change struct {
592: Line Line
593: Action string
594: Pkgpath string
595: Version string
596: Author string
597: Date string
598: }
599:
600: // SuggestedUpdate is from the `doc/TODO` file.
601: type SuggestedUpdate struct {
602: Line Line
603: Pkgname string
604: Version string
605: Comment string
606: }
CVSweb <webmaster@jp.NetBSD.org>