Received: by mail.netbsd.org (Postfix, from userid 605) id 20B4084DD2; Wed, 18 Mar 2020 08:24:54 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by mail.netbsd.org (Postfix) with ESMTP id 99FA684D4E for ; Wed, 18 Mar 2020 08:24:53 +0000 (UTC) X-Virus-Scanned: amavisd-new at netbsd.org Received: from mail.netbsd.org ([IPv6:::1]) by localhost (mail.netbsd.org [IPv6:::1]) (amavisd-new, port 10025) with ESMTP id Fb0E6tlwtdmI for ; Wed, 18 Mar 2020 08:24:50 +0000 (UTC) Received: from cvs.NetBSD.org (ivanova.netbsd.org [199.233.217.197]) by mail.netbsd.org (Postfix) with ESMTP id 5642584DB5 for ; Wed, 18 Mar 2020 08:24:50 +0000 (UTC) Received: by cvs.NetBSD.org (Postfix, from userid 500) id 4B38EFB27; Wed, 18 Mar 2020 08:24:50 +0000 (UTC) Content-Transfer-Encoding: 7bit Content-Type: multipart/mixed; boundary="_----------=_158451989062240" MIME-Version: 1.0 Date: Wed, 18 Mar 2020 08:24:50 +0000 From: "Roland Illig" Subject: CVS commit: pkgsrc/pkgtools/pkglint To: pkgsrc-changes@NetBSD.org Reply-To: rillig@netbsd.org X-Mailer: log_accum Message-Id: <20200318082450.4B38EFB27@cvs.NetBSD.org> Sender: pkgsrc-changes-owner@NetBSD.org List-Id: pkgsrc-changes.NetBSD.org Precedence: bulk List-Unsubscribe: This is a multi-part message in MIME format. --_----------=_158451989062240 Content-Disposition: inline Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII" Module Name: pkgsrc Committed By: rillig Date: Wed Mar 18 08:24:49 UTC 2020 Modified Files: pkgsrc/pkgtools/pkglint: Makefile pkgsrc/pkgtools/pkglint/files: buildlink3.go buildlink3_test.go check_test.go mkassignchecker_test.go mklines.go mkvarusechecker_test.go options.go package.go package_test.go pkglint.go pkglint_test.go pkgsrc.go pkgsrc_test.go plist.go plist_test.go shell.go shell_test.go util.go util_test.go vardefs.go vardefs_test.go vartypecheck.go vartypecheck_test.go Log Message: pkgtools/pkglint: update to 19.4.12 Changes since 19.4.11: Redundant additions to BUILDLINK_API_DEPENDS and BUILDLINK_ABI_DEPENDS get warnings since they may have been needed in the past but the dependent package has increased its required version numbers over time. To generate a diff of this commit: cvs rdiff -u -r1.634 -r1.635 pkgsrc/pkgtools/pkglint/Makefile cvs rdiff -u -r1.33 -r1.34 pkgsrc/pkgtools/pkglint/files/buildlink3.go cvs rdiff -u -r1.42 -r1.43 pkgsrc/pkgtools/pkglint/files/buildlink3_test.go cvs rdiff -u -r1.67 -r1.68 pkgsrc/pkgtools/pkglint/files/check_test.go cvs rdiff -u -r1.4 -r1.5 \ pkgsrc/pkgtools/pkglint/files/mkassignchecker_test.go cvs rdiff -u -r1.69 -r1.70 pkgsrc/pkgtools/pkglint/files/mklines.go cvs rdiff -u -r1.8 -r1.9 \ pkgsrc/pkgtools/pkglint/files/mkvarusechecker_test.go cvs rdiff -u -r1.22 -r1.23 pkgsrc/pkgtools/pkglint/files/options.go cvs rdiff -u -r1.83 -r1.84 pkgsrc/pkgtools/pkglint/files/package.go cvs rdiff -u -r1.70 -r1.71 pkgsrc/pkgtools/pkglint/files/package_test.go cvs rdiff -u -r1.76 -r1.77 pkgsrc/pkgtools/pkglint/files/pkglint.go cvs rdiff -u -r1.60 -r1.61 pkgsrc/pkgtools/pkglint/files/pkglint_test.go cvs rdiff -u -r1.51 -r1.52 pkgsrc/pkgtools/pkglint/files/pkgsrc.go cvs rdiff -u -r1.44 -r1.45 pkgsrc/pkgtools/pkglint/files/pkgsrc_test.go cvs rdiff -u -r1.54 -r1.55 pkgsrc/pkgtools/pkglint/files/plist.go cvs rdiff -u -r1.46 -r1.47 pkgsrc/pkgtools/pkglint/files/plist_test.go cvs rdiff -u -r1.59 -r1.60 pkgsrc/pkgtools/pkglint/files/shell.go cvs rdiff -u -r1.65 -r1.66 pkgsrc/pkgtools/pkglint/files/shell_test.go cvs rdiff -u -r1.74 -r1.75 pkgsrc/pkgtools/pkglint/files/util.go cvs rdiff -u -r1.50 -r1.51 pkgsrc/pkgtools/pkglint/files/util_test.go cvs rdiff -u -r1.90 -r1.91 pkgsrc/pkgtools/pkglint/files/vardefs.go cvs rdiff -u -r1.27 -r1.28 pkgsrc/pkgtools/pkglint/files/vardefs_test.go cvs rdiff -u -r1.82 -r1.83 pkgsrc/pkgtools/pkglint/files/vartypecheck.go cvs rdiff -u -r1.77 -r1.78 pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. --_----------=_158451989062240 Content-Disposition: inline Content-Length: 56869 Content-Transfer-Encoding: binary Content-Type: text/x-diff; charset=us-ascii Modified files: Index: pkgsrc/pkgtools/pkglint/Makefile diff -u pkgsrc/pkgtools/pkglint/Makefile:1.634 pkgsrc/pkgtools/pkglint/Makefile:1.635 --- pkgsrc/pkgtools/pkglint/Makefile:1.634 Sun Mar 15 11:31:24 2020 +++ pkgsrc/pkgtools/pkglint/Makefile Wed Mar 18 08:24:49 2020 @@ -1,6 +1,6 @@ -# $NetBSD: Makefile,v 1.634 2020/03/15 11:31:24 rillig Exp $ +# $NetBSD: Makefile,v 1.635 2020/03/18 08:24:49 rillig Exp $ -PKGNAME= pkglint-19.4.11 +PKGNAME= pkglint-19.4.12 CATEGORIES= pkgtools DISTNAME= tools MASTER_SITES= ${MASTER_SITE_GITHUB:=golang/} Index: pkgsrc/pkgtools/pkglint/files/buildlink3.go diff -u pkgsrc/pkgtools/pkglint/files/buildlink3.go:1.33 pkgsrc/pkgtools/pkglint/files/buildlink3.go:1.34 --- pkgsrc/pkgtools/pkglint/files/buildlink3.go:1.33 Sun Mar 15 11:31:24 2020 +++ pkgsrc/pkgtools/pkglint/files/buildlink3.go Wed Mar 18 08:24:49 2020 @@ -13,8 +13,8 @@ type Buildlink3Checker struct { abi, api *DependencyPattern } -func NewBuildlink3Checker(mklines *MkLines) *Buildlink3Checker { - return &Buildlink3Checker{mklines: mklines} +func CheckLinesBuildlink3Mk(mklines *MkLines) { + (&Buildlink3Checker{mklines: mklines}).Check() } func (ck *Buildlink3Checker) Check() { @@ -90,6 +90,9 @@ func (ck *Buildlink3Checker) checkFirstP mlex.SkipEmptyOrNote() ck.pkgbase = pkgbase + if pkg := ck.mklines.pkg; pkg != nil { + pkg.buildlinkID = ck.pkgbase + } ck.pkgbaseLine = pkgbaseLine return true } @@ -309,3 +312,65 @@ func (ck *Buildlink3Checker) checkVaruse "after the specific version has been decided.") } } + +type Buildlink3Data struct { + id Buildlink3ID + pkgsrcdir PackagePath + apiDepends *DependencyPattern + apiDependsLine *MkLine + abiDepends *DependencyPattern + abiDependsLine *MkLine +} + +// Buildlink3ID is the identifier that is used in the BUILDLINK_TREE +// for referring to a dependent package. +// +// It almost uniquely identifies a package. +// Packages that are alternatives to each other may use the same identifier. +type Buildlink3ID string + +func LoadBuildlink3Data(mklines *MkLines) *Buildlink3Data { + assert(mklines.lines.BaseName == "buildlink3.mk") + + var data Buildlink3Data + mklines.ForEach(func(mkline *MkLine) { + if mkline.IsVarassign() { + varname := mkline.Varname() + varbase := varnameBase(varname) + varid := Buildlink3ID(varnameParam(varname)) + + if varname == "BUILDLINK_TREE" { + value := mkline.Value() + if !hasPrefix(value, "-") { + data.id = Buildlink3ID(mkline.Value()) + } + } + + if varbase == "BUILDLINK_API_DEPENDS" && varid == data.id { + p := NewMkParser(nil, mkline.Value()) + dep := p.DependencyPattern() + if dep != nil && p.EOF() { + data.apiDepends = dep + data.apiDependsLine = mkline + } + } + + if varbase == "BUILDLINK_ABI_DEPENDS" && varid == data.id { + p := NewMkParser(nil, mkline.Value()) + dep := p.DependencyPattern() + if dep != nil && p.EOF() { + data.abiDepends = dep + data.abiDependsLine = mkline + } + } + + if varbase == "BUILDLINK_PKGSRCDIR" && varid == data.id { + data.pkgsrcdir = NewPackagePathString(mkline.Value()) + } + } + }) + if data.id != "" && !data.pkgsrcdir.IsEmpty() && data.apiDepends != nil && data.abiDepends != nil { + return &data + } + return nil +} Index: pkgsrc/pkgtools/pkglint/files/buildlink3_test.go diff -u pkgsrc/pkgtools/pkglint/files/buildlink3_test.go:1.42 pkgsrc/pkgtools/pkglint/files/buildlink3_test.go:1.43 --- pkgsrc/pkgtools/pkglint/files/buildlink3_test.go:1.42 Sun Mar 15 11:31:24 2020 +++ pkgsrc/pkgtools/pkglint/files/buildlink3_test.go Wed Mar 18 08:24:49 2020 @@ -2,10 +2,6 @@ package pkglint import "gopkg.in/check.v1" -func CheckLinesBuildlink3Mk(mklines *MkLines) { - NewBuildlink3Checker(mklines).Check() -} - // This test ensures that CheckLinesBuildlink3Mk really checks for // buildlink3.mk files that are included by the buildlink3.mk file // but not by the package. @@ -1066,3 +1062,31 @@ func (s *Suite) Test_Buildlink3Checker_c "WARN: buildlink3.mk:3: Please replace \"${LICENSE}\" with a simple string "+ "(also in other variables in this file).") } + +func (s *Suite) Test_LoadBuildlink3Data(c *check.C) { + t := s.Init(c) + + t.CreateFileBuildlink3("category/package/buildlink3.mk", + "BUILDLINK_ABI_DEPENDS.package+=\tpackage>=0.1") + t.Chdir("category/package") + mklines := LoadMk("buildlink3.mk", nil, MustSucceed) + + data := LoadBuildlink3Data(mklines) + + t.CheckDeepEquals(data, &Buildlink3Data{ + id: "package", + pkgsrcdir: "../../category/package", + apiDepends: &DependencyPattern{ + Pkgbase: "package", + LowerOp: ">=", + Lower: "0", + }, + apiDependsLine: mklines.mklines[7], + abiDepends: &DependencyPattern{ + Pkgbase: "package", + LowerOp: ">=", + Lower: "0.1", + }, + abiDependsLine: mklines.mklines[11], + }) +} Index: pkgsrc/pkgtools/pkglint/files/check_test.go diff -u pkgsrc/pkgtools/pkglint/files/check_test.go:1.67 pkgsrc/pkgtools/pkglint/files/check_test.go:1.68 --- pkgsrc/pkgtools/pkglint/files/check_test.go:1.67 Sun Mar 15 11:31:24 2020 +++ pkgsrc/pkgtools/pkglint/files/check_test.go Wed Mar 18 08:24:49 2020 @@ -243,7 +243,7 @@ func (t *Tester) SetUpVartypes() { func (t *Tester) SetUpMasterSite(varname string, urls ...string) { if !G.Pkgsrc.vartypes.IsDefinedExact(varname) { - G.Pkgsrc.vartypes.DefineParse(varname, BtFetchURL, + t.SetUpType(varname, BtFetchURL, List|SystemProvided, "buildlink3.mk: none", "*: use") @@ -613,6 +613,7 @@ func (t *Tester) CreateFileBuildlink3Id( sprintf("%s_BUILDLINK3_MK:=", upperID), "", aligned("BUILDLINK_API_DEPENDS.%s+=", id)+sprintf("%s>=0", id), + // TODO: Add ABI_DEPENDS; see Test_LoadBuildlink3Data aligned("BUILDLINK_PKGSRCDIR.%s?=", id)+sprintf("../../%s", dir), aligned("BUILDLINK_DEPMETHOD.%s?=", id)+"build", "") Index: pkgsrc/pkgtools/pkglint/files/mkassignchecker_test.go diff -u pkgsrc/pkgtools/pkglint/files/mkassignchecker_test.go:1.4 pkgsrc/pkgtools/pkglint/files/mkassignchecker_test.go:1.5 --- pkgsrc/pkgtools/pkglint/files/mkassignchecker_test.go:1.4 Sat Feb 15 13:48:40 2020 +++ pkgsrc/pkgtools/pkglint/files/mkassignchecker_test.go Wed Mar 18 08:24:49 2020 @@ -427,9 +427,9 @@ func (s *Suite) Test_MkAssignChecker_che t.SetUpVartypes() t.SetUpTool("awk", "AWK", AtRunTime) - G.Pkgsrc.vartypes.DefineParse("SET_ONLY", BtUnknown, NoVartypeOptions, + t.SetUpType("SET_ONLY", BtUnknown, NoVartypeOptions, "options.mk: set") - G.Pkgsrc.vartypes.DefineParse("SET_ONLY_DEFAULT_ELSEWHERE", BtUnknown, NoVartypeOptions, + t.SetUpType("SET_ONLY_DEFAULT_ELSEWHERE", BtUnknown, NoVartypeOptions, "options.mk: set", "*.mk: default, set") mklines := t.NewMkLines("options.mk", Index: pkgsrc/pkgtools/pkglint/files/mklines.go diff -u pkgsrc/pkgtools/pkglint/files/mklines.go:1.69 pkgsrc/pkgtools/pkglint/files/mklines.go:1.70 --- pkgsrc/pkgtools/pkglint/files/mklines.go:1.69 Sat Mar 7 23:35:35 2020 +++ pkgsrc/pkgtools/pkglint/files/mklines.go Wed Mar 18 08:24:49 2020 @@ -248,13 +248,7 @@ func (mklines *MkLines) collectDocumente } func (mklines *MkLines) collectVariables() { - if trace.Tracing { - defer trace.Call0()() - } - - mklines.ForEach(func(mkline *MkLine) { - mklines.collectVariable(mkline) - }) + mklines.ForEach(mklines.collectVariable) } func (mklines *MkLines) collectVariable(mkline *MkLine) { Index: pkgsrc/pkgtools/pkglint/files/mkvarusechecker_test.go diff -u pkgsrc/pkgtools/pkglint/files/mkvarusechecker_test.go:1.8 pkgsrc/pkgtools/pkglint/files/mkvarusechecker_test.go:1.9 --- pkgsrc/pkgtools/pkglint/files/mkvarusechecker_test.go:1.8 Sun Mar 15 11:31:24 2020 +++ pkgsrc/pkgtools/pkglint/files/mkvarusechecker_test.go Wed Mar 18 08:24:49 2020 @@ -455,9 +455,9 @@ func (s *Suite) Test_MkVarUseChecker_che t := s.Init(c) t.SetUpPkgsrc() - G.Pkgsrc.vartypes.DefineParse("LOAD_TIME", BtPathPattern, List, + t.SetUpType("LOAD_TIME", BtPathPattern, List, "special:filename.mk: use-loadtime") - G.Pkgsrc.vartypes.DefineParse("RUN_TIME", BtPathPattern, List, + t.SetUpType("RUN_TIME", BtPathPattern, List, "special:filename.mk: use") t.Chdir(".") t.FinishSetUp() @@ -479,9 +479,9 @@ func (s *Suite) Test_MkVarUseChecker_che t := s.Init(c) t.SetUpPkgsrc() - G.Pkgsrc.vartypes.DefineParse("LOAD_TIME", BtPathPattern, List, + t.SetUpType("LOAD_TIME", BtPathPattern, List, "special:filename.mk: use-loadtime") - G.Pkgsrc.vartypes.DefineParse("RUN_TIME", BtPathPattern, List, + t.SetUpType("RUN_TIME", BtPathPattern, List, "special:filename.mk: use") t.Chdir(".") t.FinishSetUp() @@ -532,16 +532,16 @@ func (s *Suite) Test_MkVarUseChecker_che t := s.Init(c) t.SetUpPkgsrc() - G.Pkgsrc.vartypes.DefineParse("LOAD_TIME", BtUnknown, NoVartypeOptions, + t.SetUpType("LOAD_TIME", BtUnknown, NoVartypeOptions, "*.mk: use, use-loadtime") - G.Pkgsrc.vartypes.DefineParse("RUN_TIME", BtUnknown, NoVartypeOptions, + t.SetUpType("RUN_TIME", BtUnknown, NoVartypeOptions, "*.mk: use") - G.Pkgsrc.vartypes.DefineParse("WRITE_ONLY", BtUnknown, NoVartypeOptions, + t.SetUpType("WRITE_ONLY", BtUnknown, NoVartypeOptions, "*.mk: set") - G.Pkgsrc.vartypes.DefineParse("LOAD_TIME_ELSEWHERE", BtUnknown, NoVartypeOptions, + t.SetUpType("LOAD_TIME_ELSEWHERE", BtUnknown, NoVartypeOptions, "Makefile: use-loadtime", "*.mk: set") - G.Pkgsrc.vartypes.DefineParse("RUN_TIME_ELSEWHERE", BtUnknown, NoVartypeOptions, + t.SetUpType("RUN_TIME_ELSEWHERE", BtUnknown, NoVartypeOptions, "Makefile: use", "*.mk: set") t.Chdir(".") @@ -637,7 +637,7 @@ func (s *Suite) Test_MkVarUseChecker_che func (s *Suite) Test_MkVarUseChecker_checkPermissions__usable_only_at_loadtime_in_other_file(c *check.C) { t := s.Init(c) - G.Pkgsrc.vartypes.DefineParse("VAR", BtFilename, NoVartypeOptions, + t.SetUpType("VAR", BtFilename, NoVartypeOptions, "*: set, use-loadtime") mklines := t.NewMkLines("Makefile", MkCvsID, @@ -656,9 +656,8 @@ func (s *Suite) Test_MkVarUseChecker_che // This combination of BtUnknown and all permissions is typical for // otherwise unknown variables from the pkgsrc infrastructure. - G.Pkgsrc.vartypes.Define("INFRA", BtUnknown, NoVartypeOptions, - NewACLEntry("*", aclpAll)) - G.Pkgsrc.vartypes.DefineParse("VAR", BtUnknown, NoVartypeOptions, + t.SetUpType("INFRA", BtUnknown, NoVartypeOptions) + t.SetUpType("VAR", BtUnknown, NoVartypeOptions, "buildlink3.mk: none", "*: use") mklines := t.NewMkLines("buildlink3.mk", @@ -692,10 +691,10 @@ func (s *Suite) Test_MkVarUseChecker_che // to use its value in LOAD_TIME, as the latter might be evaluated later // at load time, and at that point VAR would be evaluated as well. - G.Pkgsrc.vartypes.DefineParse("LOAD_TIME", BtMessage, NoVartypeOptions, + t.SetUpType("LOAD_TIME", BtMessage, NoVartypeOptions, "buildlink3.mk: set", "*.mk: use-loadtime") - G.Pkgsrc.vartypes.DefineParse("VAR", BtUnknown, NoVartypeOptions, + t.SetUpType("VAR", BtUnknown, NoVartypeOptions, "buildlink3.mk: none", "*.mk: use") mklines := t.NewMkLines("buildlink3.mk", @@ -963,7 +962,8 @@ func (s *Suite) Test_MkVarUseChecker_che t.SetUpVartypes() mklines := t.NewMkLines("filename.mk", "BUILTIN_FIND_FILES_VAR:=\tBIN_FILE", - "BUILTIN_FIND_FILES.BIN_FILE=\t${TOOLS_PLATFORM.file} /bin/file /usr/bin/file") + "BUILTIN_FIND_FILES.BIN_FILE=\t${TOOLS_PLATFORM.file} /bin/file /usr/bin/file", + "PKG_SHELL.user=\t${TOOLS_PLATFORM.false:Q}") mklines.ForEach(func(mkline *MkLine) { ck := NewMkAssignChecker(mkline, mklines) @@ -971,8 +971,11 @@ func (s *Suite) Test_MkVarUseChecker_che }) t.CheckOutputLines( - "WARN: filename.mk:2: Incompatible types: " + - "TOOLS_PLATFORM.file (type \"ShellCommand\") " + + "WARN: filename.mk:2: Incompatible types: "+ + "TOOLS_PLATFORM.file (type \"ShellCommand\") "+ + "cannot be assigned to type \"Pathname\".", + "WARN: filename.mk:3: Incompatible types: "+ + "TOOLS_PLATFORM.false (type \"ShellCommand\") "+ "cannot be assigned to type \"Pathname\".") } @@ -993,7 +996,8 @@ func (s *Suite) Test_MkVarUseChecker_che mklines := t.NewMkLines("filename.mk", "PKG_SHELL.user=\t${TOOLS_PLATFORM.sh}", "PKG_SHELL.user=\t${SH}", - "PKG_SHELL.user=\t${BASH}") + "PKG_SHELL.user=\t${BASH}", + "PKG_SHELL.user=\t/bin/sh") mklines.ForEach(func(mkline *MkLine) { ck := NewMkAssignChecker(mkline, mklines) @@ -1232,11 +1236,17 @@ func (s *Suite) Test_MkVarUseChecker_che t.SetUpTool("cond1", "", AfterPrefsMk) t.SetUpTool("cond2", "", AfterPrefsMk) t.SetUpTool("undefined", "", AfterPrefsMk) + t.SetUpTool("non-const", "", AfterPrefsMk) t.CreateFileLines("mk/tools/tools.NetBSD.mk", + "OTHER_VAR?=\tother value", // Just for code coverage "TOOLS_PLATFORM.available?=\t/bin/available", "TOOLS_PLATFORM.cond1?=\t/usr/cond1", "TOOLS_PLATFORM.cond2?=\t/usr/cond2", - "TOOLS_PLATFORM.undefined?=\t/usr/undefined") + "TOOLS_PLATFORM.undefined?=\t/usr/undefined", + "", + "TOOLS_PLATFORM.non-const?=\t/non-const-initial", + "READ=\t${TOOLS_PLATFORM.non-const}", // Make the variable non-const + "TOOLS_PLATFORM.non-const?=\t/non-const-final") t.CreateFileLines("mk/tools/tools.SunOS.mk", "TOOLS_PLATFORM.available?=\t/bin/available", "", Index: pkgsrc/pkgtools/pkglint/files/options.go diff -u pkgsrc/pkgtools/pkglint/files/options.go:1.22 pkgsrc/pkgtools/pkglint/files/options.go:1.23 --- pkgsrc/pkgtools/pkglint/files/options.go:1.22 Sun Mar 15 11:31:24 2020 +++ pkgsrc/pkgtools/pkglint/files/options.go Wed Mar 18 08:24:49 2020 @@ -53,7 +53,7 @@ func (ck *OptionsLinesChecker) collect() seenPkgOptionsVar = true buildlinkID := ck.buildlinkID optionsID := varnameParam(mkline.Value()) - if buildlinkID != "" && optionsID != "" && buildlinkID != optionsID { + if buildlinkID != "" && buildlinkID != optionsID { mkline.Warnf("The buildlink3 identifier %q should be the same as the options identifier %q.", buildlinkID, optionsID) mkline.Explain( Index: pkgsrc/pkgtools/pkglint/files/package.go diff -u pkgsrc/pkgtools/pkglint/files/package.go:1.83 pkgsrc/pkgtools/pkglint/files/package.go:1.84 --- pkgsrc/pkgtools/pkglint/files/package.go:1.83 Sun Mar 15 11:31:24 2020 +++ pkgsrc/pkgtools/pkglint/files/package.go Wed Mar 18 08:24:49 2020 @@ -43,6 +43,9 @@ type Package struct { // These included files should match. bl3 map[PackagePath]*MkLine + // The default dependencies from the buildlink3.mk files. + bl3Data map[Buildlink3ID]*Buildlink3Data + // Remembers the Makefile fragments that have already been included. // The key to the map is the filename relative to the package directory. // Typical keys are "../../category/package/buildlink3.mk". @@ -103,6 +106,7 @@ func NewPackage(dir CurrPath) *Package { Plist: NewPlistContent(), vars: NewScope(), bl3: make(map[PackagePath]*MkLine), + bl3Data: make(map[Buildlink3ID]*Buildlink3Data), included: Once{}, conditionalIncludes: make(map[PackagePath]*MkLine), unconditionalIncludes: make(map[PackagePath]*MkLine), @@ -167,11 +171,7 @@ func (pkg *Package) load() ([]CurrPath, if isRelevantMk(filename, basename) { fragmentMklines := LoadMk(filename, pkg, MustSucceed) pkg.collectConditionalIncludes(fragmentMklines) - fragmentMklines.ForEach(func(mkline *MkLine) { - if mkline.IsVarassign() && mkline.Varname() == "pkgbase" { - pkg.seenPkgbase.FirstTime(mkline.Value()) - } - }) + pkg.loadBuildlink3Pkgbase(filename, fragmentMklines) } if hasPrefix(basename, "PLIST") { pkg.loadPlistDirs(filename) @@ -182,6 +182,17 @@ func (pkg *Package) load() ([]CurrPath, return files, mklines, allLines } +func (pkg *Package) loadBuildlink3Pkgbase(filename CurrPath, fragmentMklines *MkLines) { + if !filename.HasBase("buildlink3.mk") { + return + } + fragmentMklines.ForEach(func(mkline *MkLine) { + if mkline.IsVarassign() && mkline.Varname() == "pkgbase" { + pkg.seenPkgbase.FirstTime(mkline.Value()) + } + }) +} + func (pkg *Package) loadPackageMakefile() (*MkLines, *MkLines) { filename := pkg.File("Makefile") if trace.Tracing { @@ -271,13 +282,18 @@ func (pkg *Package) parse(mklines *MkLin // For every included buildlink3.mk, include the corresponding builtin.mk // automatically since the pkgsrc infrastructure does the same. filename := mklines.lines.Filename - if filename.Base() == "buildlink3.mk" { + if mklines.lines.BaseName == "buildlink3.mk" { builtin := filename.Dir().JoinNoClean("builtin.mk").CleanPath() builtinRel := G.Pkgsrc.Relpath(pkg.dir, builtin) if pkg.included.FirstTime(builtinRel.String()) && builtin.IsFile() { builtinMkLines := LoadMk(builtin, pkg, MustSucceed|LogErrors) pkg.parse(builtinMkLines, allLines, "", false) } + + data := LoadBuildlink3Data(mklines) + if data != nil { + pkg.bl3Data[data.id] = data + } } return result Index: pkgsrc/pkgtools/pkglint/files/package_test.go diff -u pkgsrc/pkgtools/pkglint/files/package_test.go:1.70 pkgsrc/pkgtools/pkglint/files/package_test.go:1.71 --- pkgsrc/pkgtools/pkglint/files/package_test.go:1.70 Sun Mar 15 11:31:24 2020 +++ pkgsrc/pkgtools/pkglint/files/package_test.go Wed Mar 18 08:24:49 2020 @@ -516,6 +516,33 @@ func (s *Suite) Test_Package_load__extra "WARN: patches/readme.mk: Patch files should be named \"patch-\", followed by letters, '-', '_', '.', and digits only.") } +func (s *Suite) Test_Package_loadBuildlink3Pkgbase(c *check.C) { + t := s.Init(c) + + t.SetUpPackage("category/package", + ".include \"../../category/lib/buildlink3.mk\"") + t.CreateFileBuildlink3("category/package/buildlink3.mk", + "pkgbase := package", + ".include \"../../mk/pkg-build-options.mk\"", + ".include \"../../category/lib/buildlink3.mk\"") + t.SetUpPackage("category/lib") + t.CreateFileBuildlink3("category/lib/buildlink3.mk", + "pkgbase := lib", + ".include \"../../mk/pkg-build-options.mk\"") + t.CreateFileLines("mk/pkg-build-options.mk") + t.Chdir("category/package") + t.FinishSetUp() + pkg := NewPackage(".") + + pkg.Check() + + t.CheckOutputEmpty() + seenPkgbase := pkg.seenPkgbase + t.Check(seenPkgbase.seen, check.HasLen, 2) + t.CheckEquals(seenPkgbase.Seen("lib"), true) + t.CheckEquals(seenPkgbase.Seen("package"), true) +} + func (s *Suite) Test_Package_loadPackageMakefile__dump(c *check.C) { t := s.Init(c) Index: pkgsrc/pkgtools/pkglint/files/pkglint.go diff -u pkgsrc/pkgtools/pkglint/files/pkglint.go:1.76 pkgsrc/pkgtools/pkglint/files/pkglint.go:1.77 --- pkgsrc/pkgtools/pkglint/files/pkglint.go:1.76 Sun Mar 15 11:31:24 2020 +++ pkgsrc/pkgtools/pkglint/files/pkglint.go Wed Mar 18 08:24:49 2020 @@ -571,11 +571,7 @@ func (p *Pkglint) checkReg(filename Curr case basename == "buildlink3.mk": if mklines := LoadMk(filename, pkg, NotEmpty|LogErrors); mklines != nil { - ck := NewBuildlink3Checker(mklines) - ck.Check() - if pkg != nil { - pkg.buildlinkID = ck.pkgbase - } + CheckLinesBuildlink3Mk(mklines) } case hasPrefix(basename, "DESCR"): Index: pkgsrc/pkgtools/pkglint/files/pkglint_test.go diff -u pkgsrc/pkgtools/pkglint/files/pkglint_test.go:1.60 pkgsrc/pkgtools/pkglint/files/pkglint_test.go:1.61 --- pkgsrc/pkgtools/pkglint/files/pkglint_test.go:1.60 Sat Feb 15 13:48:40 2020 +++ pkgsrc/pkgtools/pkglint/files/pkglint_test.go Wed Mar 18 08:24:49 2020 @@ -740,6 +740,23 @@ func (s *Suite) Test_resolveVariableRefs t.CheckEquals(resolved, "gst-plugins0.10-x11/distinfo") } +func (s *Suite) Test_resolveVariableRefs__indeterminate(c *check.C) { + t := s.Init(c) + + pkg := NewPackage(G.Pkgsrc.File("category/package")) + pkg.vars.Define("PKGVAR", t.NewMkLine("filename.mk", 123, "PKGVAR!=\tcommand")) + mklines := t.NewMkLinesPkg("filename.mk", pkg, + "VAR!=\tcommand") + mklines.collectVariables() + + resolved := resolveVariableRefs("${VAR} ${PKGVAR}", mklines, nil) + + // VAR and PKGVAR are defined, but since they contain the result of + // a shell command, their value is indeterminate. + // Therefore they are not replaced. + t.CheckEquals(resolved, "${VAR} ${PKGVAR}") +} + // Just for code coverage. func (s *Suite) Test_CheckFileOther__no_tracing(c *check.C) { t := s.Init(c) @@ -1106,6 +1123,24 @@ func (s *Suite) Test_Pkglint_checkReg__s "WARN: ~/category/package/spec: Only packages in regress/ may have spec files.") } +func (s *Suite) Test_Pkglint_checkReg__options_mk(c *check.C) { + t := s.Init(c) + + t.SetUpPkgsrc() + t.CreateFileLines("mk/bsd.options.mk") + t.CreateFileLines("category/package/options.mk", + MkCvsID, + "", + "PKG_OPTIONS_VAR=\tPKG_OPTIONS.package", + "", + ".include \"../../mk/bsd.options.mk\"") + t.FinishSetUp() + + G.Check(t.File("category/package/options.mk")) + + t.CheckOutputEmpty() +} + func (s *Suite) Test_Pkglint_checkRegCvsSubst(c *check.C) { t := s.Init(c) Index: pkgsrc/pkgtools/pkglint/files/pkgsrc.go diff -u pkgsrc/pkgtools/pkglint/files/pkgsrc.go:1.51 pkgsrc/pkgtools/pkglint/files/pkgsrc.go:1.52 --- pkgsrc/pkgtools/pkglint/files/pkgsrc.go:1.51 Sat Mar 7 23:35:35 2020 +++ pkgsrc/pkgtools/pkglint/files/pkgsrc.go Wed Mar 18 08:24:49 2020 @@ -14,8 +14,8 @@ import ( // 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. +// Everything else (distfile hashes, package names) is recorded in the +// InterPackage type instead. type Pkgsrc struct { // The top directory (PKGSRCDIR). topdir CurrPath @@ -530,11 +530,8 @@ func (src *Pkgsrc) loadToolsPlatform() { conditional[opsys] = true } for opsys, status := range statusByNameAndOpsys[name] { - switch status { - case 1: - delete(undefined, opsys) - case 2: - delete(undefined, opsys) + delete(undefined, opsys) + if status == 2 { delete(conditional, opsys) } } @@ -1090,6 +1087,7 @@ func (src *Pkgsrc) ReadDir(dirName Pkgsr var relevantFiles []os.FileInfo for _, dirent := range files { name := dirent.Name() + // TODO: defer isEmptyDir, for performance reasons; run ktrace or strace to see why. if !dirent.IsDir() || !isIgnoredFilename(name) && !isEmptyDir(dir.JoinNoClean(NewRelPathString(name))) { relevantFiles = append(relevantFiles, dirent) } Index: pkgsrc/pkgtools/pkglint/files/pkgsrc_test.go diff -u pkgsrc/pkgtools/pkglint/files/pkgsrc_test.go:1.44 pkgsrc/pkgtools/pkglint/files/pkgsrc_test.go:1.45 --- pkgsrc/pkgtools/pkglint/files/pkgsrc_test.go:1.44 Mon Feb 17 20:22:21 2020 +++ pkgsrc/pkgtools/pkglint/files/pkgsrc_test.go Wed Mar 18 08:24:49 2020 @@ -782,6 +782,19 @@ func (s *Suite) Test_Pkgsrc_loadTools__n "FATAL: ~/mk/tools/bsd.tools.mk: Too few tool files.") } +// Just for code coverage, for the IsRelevant callback. +func (s *Suite) Test_Pkgsrc_loadToolsPlatform__redundant(c *check.C) { + t := s.Init(c) + + t.SetUpPkgsrc() + t.SetUpTool("tool", "", AfterPrefsMk) + t.CreateFileLines("mk/tools/tools.NetBSD.mk", + "TOOLS_PLATFORM.tool?=\t/bin/available", + "TOOLS_PLATFORM.tool?=\t/bin/available") + t.Chdir(".") + t.FinishSetUp() +} + func (s *Suite) Test_Pkgsrc_initDeprecatedVars(c *check.C) { t := s.Init(c) Index: pkgsrc/pkgtools/pkglint/files/plist.go diff -u pkgsrc/pkgtools/pkglint/files/plist.go:1.54 pkgsrc/pkgtools/pkglint/files/plist.go:1.55 --- pkgsrc/pkgtools/pkglint/files/plist.go:1.54 Mon Feb 17 20:22:21 2020 +++ pkgsrc/pkgtools/pkglint/files/plist.go Wed Mar 18 08:24:49 2020 @@ -528,7 +528,9 @@ type PlistLine struct { text string // Line.Text without any conditions of the form ${PLIST.cond} } -func (pline *PlistLine) Path() RelPath { return NewRelPathString(pline.text) } +func (pline *PlistLine) HasPath() bool { + return pline.text != "" && plistLineStart.Contains(pline.text[0]) +} func (pline *PlistLine) HasPlainPath() bool { text := pline.text @@ -537,6 +539,11 @@ func (pline *PlistLine) HasPlainPath() b !containsVarUse(text) } +func (pline *PlistLine) Path() RelPath { + assert(pline.HasPath()) + return NewRelPathString(pline.text) +} + func (pline *PlistLine) CheckTrailingWhitespace() { if hasSuffix(pline.text, " ") || hasSuffix(pline.text, "\t") { pline.Errorf("Pkgsrc does not support filenames ending in whitespace.") @@ -687,3 +694,53 @@ func (s *plistLineSorter) Sort() { s.autofixed = SaveAutofixChanges(NewLines(lines[0].Filename(), lines)) } + +type PlistRank uint8 + +const ( + Plain PlistRank = iota + Common + CommonEnd + Opsys + Arch + OpsysArch + EmulOpsysArch +) + +// The ranks among the files are: +// PLIST +// -> PLIST.common +// -> PLIST.common_end +// -> { PLIST.OPSYS, PLIST.ARCH } +// -> { PLIST.OPSYS.ARCH, PLIST.EMUL_PLATFORM } +// Files are a later level must not mention files that are already +// mentioned at an earlier level. +func (r PlistRank) Dominates(other PlistRank) bool { + return r <= other && + !(r == Opsys && other == Arch) && + !(r == OpsysArch && other == EmulOpsysArch) +} + +type PlistLines struct { + all map[RelPath][]*plistLineData +} + +func NewPlistLines() *PlistLines { + return &PlistLines{make(map[RelPath][]*plistLineData)} +} + +type plistLineData struct { + line *PlistLine + rank PlistRank +} + +func (pl *PlistLines) Add(line *PlistLine, rank PlistRank) { + path := line.Path() + for _, existing := range pl.all[path] { + if existing.rank.Dominates(rank) { + line.Errorf("Path %s is already listed in %s.", + path, line.RelLine(existing.line.Line)) + } + } + pl.all[path] = append(pl.all[path], &plistLineData{line, rank}) +} Index: pkgsrc/pkgtools/pkglint/files/plist_test.go diff -u pkgsrc/pkgtools/pkglint/files/plist_test.go:1.46 pkgsrc/pkgtools/pkglint/files/plist_test.go:1.47 --- pkgsrc/pkgtools/pkglint/files/plist_test.go:1.46 Mon Feb 17 20:22:21 2020 +++ pkgsrc/pkgtools/pkglint/files/plist_test.go Wed Mar 18 08:24:49 2020 @@ -731,6 +731,56 @@ func (s *Suite) Test_PlistChecker_checkD "already appeared in line 2.") } +func (s *Suite) Test_PlistChecker_checkDuplicate__OPSYS(c *check.C) { + t := s.Init(c) + + t.SetUpPackage("category/package") + t.Chdir("category/package") + t.CreateFileLines("PLIST", + PlistCvsID, + "bin/common", + "bin/common_end", + "${PLIST.cond}bin/conditional", + "bin/plist") + t.CreateFileLines("PLIST.Linux", + PlistCvsID, + "bin/common", + "bin/common_end", + "${PLIST.cond}bin/conditional", + "bin/os-specific", + "bin/plist") + t.CreateFileLines("PLIST.NetBSD", + PlistCvsID, + "bin/common", + "bin/common_end", + "${PLIST.cond}bin/conditional", + "bin/os-specific", + "bin/plist") + t.CreateFileLines("PLIST.common", + PlistCvsID, + "bin/common", + "${PLIST.cond}bin/conditional") + t.CreateFileLines("PLIST.common_end", + PlistCvsID, + "bin/common_end", + "${PLIST.cond}bin/conditional") + t.FinishSetUp() + + // TODO: Use the same order as in PLIST_SRC_DFLT, see mk/plist/plist.mk. + // PLIST.common + // PLIST.${OPSYS} + // PLIST.${MACHINE_ARCH:C/i[3-6]86/i386/g} + // PLIST.${OPSYS}-${MACHINE_ARCH:C/i[3-6]86/i386/g} + // ${defined(EMUL_PLATFORM):?PLIST.${EMUL_PLATFORM}:} + // PLIST + // PLIST.common_end + // + G.Check(".") + + // TODO: Warn that bin/program is duplicate, but not bin/os-specific. + t.CheckOutputEmpty() +} + func (s *Suite) Test_PlistChecker_checkPathBin(c *check.C) { t := s.Init(c) @@ -1158,15 +1208,25 @@ func (s *Suite) Test_PlistChecker_checkO nil...) } -func (s *Suite) Test_PlistLine_Path(c *check.C) { +func (s *Suite) Test_PlistLine_HasPath(c *check.C) { t := s.Init(c) - t.CheckEquals( - (&PlistLine{text: "relative"}).Path(), - NewRelPathString("relative")) + test := func(text string, hasPath bool) { + t.CheckEquals((&PlistLine{text: text}).HasPath(), hasPath) + } - t.ExpectAssert( - func() { (&PlistLine{text: "/absolute"}).Path() }) + test("abc", true) + test("9plan", true) + test("bin/program", true) + + test("", false) + test("@", false) + test(":", false) + test("/absolute", false) + test("-rf", false) + test("\\", false) + test("bin/$<", true) + test("bin/${VAR}", true) } func (s *Suite) Test_PlistLine_HasPlainPath(c *check.C) { @@ -1190,6 +1250,17 @@ func (s *Suite) Test_PlistLine_HasPlainP test("bin/${VAR}", false) } +func (s *Suite) Test_PlistLine_Path(c *check.C) { + t := s.Init(c) + + t.CheckEquals( + (&PlistLine{text: "relative"}).Path(), + NewRelPathString("relative")) + + t.ExpectAssert( + func() { (&PlistLine{text: "/absolute"}).Path() }) +} + func (s *Suite) Test_PlistLine_CheckTrailingWhitespace(c *check.C) { t := s.Init(c) @@ -1328,3 +1399,56 @@ func (s *Suite) Test_plistLineSorter_Sor "sbin/program", "@exec echo \"after lib/after.la\"") // The footer starts here } + +func (s *Suite) Test_PlistRank_Dominates(c *check.C) { + var rel relation + rel.add(Plain, Common) + rel.add(Common, CommonEnd) + rel.add(CommonEnd, Opsys) + rel.add(CommonEnd, Arch) + rel.add(Opsys, OpsysArch) + rel.add(Opsys, EmulOpsysArch) + rel.add(Arch, OpsysArch) + rel.add(Arch, EmulOpsysArch) + rel.reflexive = true + rel.transitive = true + rel.antisymmetric = true + + rel.check(func(a, b int) bool { return PlistRank(a).Dominates(PlistRank(b)) }) +} + +func (s *Suite) Test_NewPlistLines(c *check.C) { + lines := NewPlistLines() + + c.Check(lines.all, check.NotNil) +} + +func (s *Suite) Test_PlistLines_Add(c *check.C) { + t := s.Init(c) + + t.SetUpFileLines("PLIST", + PlistCvsID, + "bin/program") + t.SetUpFileLines("PLIST.common", + PlistCvsID, + "bin/program") + plistLines := NewPlistChecker(nil).Load(Load(t.File("PLIST"), MustSucceed)) + plistCommonLines := NewPlistChecker(nil).Load(Load(t.File("PLIST.common"), MustSucceed)) + lines := NewPlistLines() + + for _, line := range plistLines { + if line.HasPath() { + lines.Add(line, Plain) + } + } + for _, line := range plistCommonLines { + if line.HasPath() { + lines.Add(line, Common) + } + } + + t.CheckOutputLines( + // TODO: Wrong order. The diagnostics should be in the same order + // as in mk/plist/plist.mk. + "ERROR: ~/PLIST.common:2: Path bin/program is already listed in PLIST:2.") +} Index: pkgsrc/pkgtools/pkglint/files/shell.go diff -u pkgsrc/pkgtools/pkglint/files/shell.go:1.59 pkgsrc/pkgtools/pkglint/files/shell.go:1.60 --- pkgsrc/pkgtools/pkglint/files/shell.go:1.59 Sat Mar 7 23:35:35 2020 +++ pkgsrc/pkgtools/pkglint/files/shell.go Wed Mar 18 08:24:49 2020 @@ -57,8 +57,6 @@ func (scc *SimpleCommandChecker) checkCo break case hasPrefix(shellword, "./"): // All commands from the current directory are fine. break - case matches(shellword, `\$\{(PKGSRCDIR|PREFIX)(:Q)?\}`): - break default: if G.WarnExtra && !scc.mklines.indentation.DependsOn("OPSYS") { scc.mkline.Warnf("Unknown shell command %q.", shellword) Index: pkgsrc/pkgtools/pkglint/files/shell_test.go diff -u pkgsrc/pkgtools/pkglint/files/shell_test.go:1.65 pkgsrc/pkgtools/pkglint/files/shell_test.go:1.66 --- pkgsrc/pkgtools/pkglint/files/shell_test.go:1.65 Sat Mar 7 23:35:35 2020 +++ pkgsrc/pkgtools/pkglint/files/shell_test.go Wed Mar 18 08:24:49 2020 @@ -145,6 +145,7 @@ func (s *Suite) Test_SimpleCommandChecke func (s *Suite) Test_SimpleCommandChecker_handleCommandVariable(c *check.C) { t := s.Init(c) + t.SetUpVartypes() t.SetUpTool("runtime", "RUNTIME", AtRunTime) t.SetUpTool("nowhere", "NOWHERE", Nowhere) mklines := t.NewMkLines("Makefile", @@ -157,7 +158,8 @@ func (s *Suite) Test_SimpleCommandChecke "", "pre-configure:", "\t: ${RUNTIME_Q_CMD} ${NOWHERE_Q_CMD}", - "\t: ${RUNTIME_CMD} ${NOWHERE_CMD}") + "\t: ${RUNTIME_CMD} ${NOWHERE_CMD}", + "\t${PKGNAME}") // This doesn't make sense; it's just for code coverage mklines.Check() @@ -168,7 +170,8 @@ func (s *Suite) Test_SimpleCommandChecke // TODO: Add a warning that in lines 3 and 4, the :Q is wrong. t.CheckOutputLines( "WARN: Makefile:4: The \"${NOWHERE:Q}\" tool is used but not added to USE_TOOLS.", - "WARN: Makefile:6: The \"${NOWHERE}\" tool is used but not added to USE_TOOLS.") + "WARN: Makefile:6: The \"${NOWHERE}\" tool is used but not added to USE_TOOLS.", + "WARN: Makefile:11: Unknown shell command \"${PKGNAME}\".") } func (s *Suite) Test_SimpleCommandChecker_handleCommandVariable__parameterized(c *check.C) { Index: pkgsrc/pkgtools/pkglint/files/util.go diff -u pkgsrc/pkgtools/pkglint/files/util.go:1.74 pkgsrc/pkgtools/pkglint/files/util.go:1.75 --- pkgsrc/pkgtools/pkglint/files/util.go:1.74 Sat Mar 7 23:35:35 2020 +++ pkgsrc/pkgtools/pkglint/files/util.go Wed Mar 18 08:24:49 2020 @@ -555,7 +555,8 @@ type Once struct { } func (o *Once) FirstTime(what string) bool { - firstTime := o.check(o.keyString(what)) + key := o.keyString(what) + firstTime := o.check(key) if firstTime && o.Trace { G.Logger.out.WriteLine(sprintf("FirstTime: %s", what)) } @@ -563,7 +564,8 @@ func (o *Once) FirstTime(what string) bo } func (o *Once) FirstTimeSlice(whats ...string) bool { - firstTime := o.check(o.keyStrings(whats)) + key := o.keyStrings(whats) + firstTime := o.check(key) if firstTime && o.Trace { G.Logger.out.WriteLine(sprintf("FirstTime: %s", strings.Join(whats, ", "))) } @@ -1260,6 +1262,8 @@ func joinOxford(conn string, elements .. return strings.Join(nonempty, ", ") } +var pathMatchers = make(map[string]*pathMatcher) + type pathMatcher struct { matchType pathMatchType pattern string @@ -1267,6 +1271,15 @@ type pathMatcher struct { } func newPathMatcher(pattern string) *pathMatcher { + matcher := pathMatchers[pattern] + if matcher == nil { + matcher = newPathMatcherUncached(pattern) + pathMatchers[pattern] = matcher + } + return matcher +} + +func newPathMatcherUncached(pattern string) *pathMatcher { assert(strings.IndexByte(pattern, '[') == -1) assert(strings.IndexByte(pattern, '?') == -1) Index: pkgsrc/pkgtools/pkglint/files/util_test.go diff -u pkgsrc/pkgtools/pkglint/files/util_test.go:1.50 pkgsrc/pkgtools/pkglint/files/util_test.go:1.51 --- pkgsrc/pkgtools/pkglint/files/util_test.go:1.50 Sun Mar 15 11:31:24 2020 +++ pkgsrc/pkgtools/pkglint/files/util_test.go Wed Mar 18 08:24:49 2020 @@ -1267,3 +1267,93 @@ func (s *Suite) Test_interval(c *check.C t.CheckEquals(i.min, -5) t.CheckEquals(i.max, 7) } + +type relation struct { + pairs []struct{ a, b int } + reflexive bool + transitive bool + antisymmetric bool +} + +func (r *relation) add(a interface{}, b interface{}) { + ia := int(reflect.ValueOf(a).Uint()) + ib := int(reflect.ValueOf(b).Uint()) + r.pairs = append(r.pairs, struct{ a, b int }{ia, ib}) +} + +func (r *relation) size() int { + max := 0 + for _, pair := range r.pairs { + if pair.a > max { + max = pair.a + } + if pair.b > max { + max = pair.b + } + } + return max + 1 +} + +func (r *relation) check(actual func(int, int) bool) { + n := r.size() + rel := make([][]bool, n) + for i := 0; i < n; i++ { + rel[i] = make([]bool, n) + } + + if r.reflexive { + for i := 0; i < n; i++ { + rel[i][i] = true + } + } + + for _, pair := range r.pairs { + rel[pair.a][pair.b] = true + } + + if r.transitive { + for { + changed := false + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + for k := 0; k < n; k++ { + if rel[i][j] && rel[j][k] && !rel[i][k] { + rel[i][k] = true + changed = true + } + } + } + } + if !changed { + break + } + } + } + + if r.antisymmetric { + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + if i != j && rel[i][j] && rel[j][i] { + panic(sprintf( + "the antisymmetric relation must not contain "+ + "both (%[1]d, %[2]d) and (%[2]d, %[1]d)", + i, j)) + } + } + } + } + + actualRel := make([][]bool, n) + for i := 0; i < n; i++ { + actualRel[i] = make([]bool, n) + for j := 0; j < n; j++ { + actualRel[i][j] = actual(i, j) + } + } + + if sprintf("%#v", rel) != sprintf("%#v", actualRel) { + // The line breaks at these positions make the two relations + // visually comparable in the output. + panic(sprintf("the relation must be\n%#v, not \n%#v", rel, actualRel)) + } +} Index: pkgsrc/pkgtools/pkglint/files/vardefs.go diff -u pkgsrc/pkgtools/pkglint/files/vardefs.go:1.90 pkgsrc/pkgtools/pkglint/files/vardefs.go:1.91 --- pkgsrc/pkgtools/pkglint/files/vardefs.go:1.90 Sat Mar 7 23:35:35 2020 +++ pkgsrc/pkgtools/pkglint/files/vardefs.go Wed Mar 18 08:24:49 2020 @@ -29,10 +29,11 @@ import ( type VarTypeRegistry struct { types map[string]*Vartype // varcanon => type + cache map[string][]ACLEntry } func NewVarTypeRegistry() VarTypeRegistry { - return VarTypeRegistry{make(map[string]*Vartype)} + return VarTypeRegistry{make(map[string]*Vartype), make(map[string][]ACLEntry)} } func (reg *VarTypeRegistry) Canon(varname string) *Vartype { @@ -55,10 +56,15 @@ func (reg *VarTypeRegistry) DefineType(v reg.types[varcanon] = vartype } -func (reg *VarTypeRegistry) Define(varname string, basicType *BasicType, options vartypeOptions, aclEntries ...ACLEntry) { +func (reg *VarTypeRegistry) Define(varname string, basicType *BasicType, options vartypeOptions, aclEntries []ACLEntry) { m, varbase, varparam := match2(varname, `^([A-Z_.][A-Z0-9_]*|@|\.newline)(|\*|\.\*)$`) assert(m) // invalid variable name + // If this assertion fails, it usually means that + // the test calls SetUpVartypes redundantly. + // For example, it is called by SetUpPkgsrc or SetUpPackage as well. + assertf(!reg.IsDefinedExact(varname), "Variable %q must only be defined once.", varname) + vartype := NewVartype(basicType, options, aclEntries...) if varparam == "" || varparam == "*" { @@ -69,7 +75,14 @@ func (reg *VarTypeRegistry) Define(varna } } -// DefineParse defines a variable with the given type and permissions. +func (reg *VarTypeRegistry) DefineName(varname string, basicType *BasicType, options vartypeOptions, aclName string) { + aclEntries := reg.cache[aclName] + assertNotNil(aclEntries) + reg.Define(varname, basicType, options, aclEntries) +} + +// acl defines the permissions of a variable by listing the permissions +// individually. // // A permission entry looks like this: // "Makefile, Makefile.*, *.mk: default, set, append, use, use-loadtime" @@ -77,30 +90,18 @@ func (reg *VarTypeRegistry) Define(varna // to prevent typos. To use arbitrary filenames, prefix them with // "special:". // -// TODO: When prefixed with "infra:", the entry should only -// apply to files within the pkgsrc infrastructure. Without this prefix, -// the pattern should only apply to files outside the pkgsrc infrastructure. -func (reg *VarTypeRegistry) DefineParse(varname string, basicType *BasicType, options vartypeOptions, aclEntries ...string) { - parsedEntries := reg.parseACLEntries(varname, aclEntries...) - reg.Define(varname, basicType, options, parsedEntries...) -} - -// acl defines the permissions of a variable by listing the permissions -// individually. -// // Each variable that uses this function directly must document: // - which of the predefined permission sets is the closest // - how this individual permission set differs // - why the predefined permission set is not good enough // - which packages need this custom permission set. +// +// TODO: When prefixed with "infra:", the entry should only +// apply to files within the pkgsrc infrastructure. Without this prefix, +// the pattern should only apply to files outside the pkgsrc infrastructure. func (reg *VarTypeRegistry) acl(varname string, basicType *BasicType, options vartypeOptions, aclEntries ...string) { - - // If this assertion fails, it usually means that - // the test calls SetUpVartypes redundantly. - // For example, it is called by SetUpPkgsrc or SetUpPackage as well. - assertf(!reg.IsDefinedExact(varname), "Variable %q must only be defined once.", varname) - - reg.DefineParse(varname, basicType, options, aclEntries...) + parsedEntries := reg.parseACLEntries(varname, aclEntries...) + reg.Define(varname, basicType, options, parsedEntries) } // acllist defines the permissions of a list variable by listing @@ -117,27 +118,17 @@ func (reg *VarTypeRegistry) acllist(varn // A package-settable variable may be set in all Makefiles except buildlink3.mk and builtin.mk. func (reg *VarTypeRegistry) pkg(varname string, basicType *BasicType) { - reg.acl(varname, basicType, - PackageSettable, - "buildlink3.mk, builtin.mk: none", - "Makefile, Makefile.*, *.mk: default, set, use") + reg.DefineName(varname, basicType, PackageSettable, "pkg") } // Like pkg, but always needs a rationale. func (reg *VarTypeRegistry) pkgrat(varname string, basicType *BasicType) { - reg.acl(varname, basicType, - PackageSettable|NeedsRationale, - "buildlink3.mk, builtin.mk: none", - "Makefile, Makefile.*, *.mk: default, set, use") + reg.DefineName(varname, basicType, PackageSettable|NeedsRationale, "pkg") } // pkgload is the same as pkg, except that the variable may be accessed at load time. func (reg *VarTypeRegistry) pkgload(varname string, basicType *BasicType) { - reg.acl(varname, basicType, - PackageSettable, - "buildlink3.mk: none", - "builtin.mk: use, use-loadtime", - "Makefile, Makefile.*, *.mk: default, set, use, use-loadtime") + reg.DefineName(varname, basicType, PackageSettable, "pkgload") } // A package-defined list may be defined and appended to in all Makefiles @@ -146,27 +137,18 @@ func (reg *VarTypeRegistry) pkgload(varn // assignment overriding a previous value, the redundancy check will // catch it. func (reg *VarTypeRegistry) pkglist(varname string, basicType *BasicType) { - reg.acllist(varname, basicType, - List|PackageSettable, - "buildlink3.mk, builtin.mk: none", - "Makefile, Makefile.*, *.mk: default, set, append, use") + reg.DefineName(varname, basicType, List|PackageSettable, "pkglist") } // Like pkglist, but always needs a rationale. func (reg *VarTypeRegistry) pkglistrat(varname string, basicType *BasicType) { - reg.acllist(varname, basicType, - List|PackageSettable|NeedsRationale, - "buildlink3.mk, builtin.mk: none", - "Makefile, Makefile.*, *.mk: default, set, append, use") + reg.DefineName(varname, basicType, List|PackageSettable|NeedsRationale, "pkglist") } // Like pkglist, but only one value per line should be given. // Typical example: PKG_FAIL_REASON. func (reg *VarTypeRegistry) pkglistone(varname string, basicType *BasicType) { - reg.acllist(varname, basicType, - List|PackageSettable|OnePerLine, - "buildlink3.mk, builtin.mk: none", - "Makefile, Makefile.*, *.mk: default, set, append, use") + reg.DefineName(varname, basicType, List|PackageSettable|OnePerLine, "pkglist") } // A package-defined load-time list may be used or defined or appended to in @@ -174,10 +156,7 @@ func (reg *VarTypeRegistry) pkglistone(v // (instead of appending) is also allowed. If this leads to an unconditional // assignment overriding a previous value, the redundancy check will catch it. func (reg *VarTypeRegistry) pkgloadlist(varname string, basicType *BasicType) { - reg.acllist(varname, basicType, - List|PackageSettable, - "buildlink3.mk, builtin.mk: none", - "Makefile, Makefile.*, *.mk: default, set, append, use, use-loadtime") + reg.DefineName(varname, basicType, List|PackageSettable, "pkgloadlist") } // pkgappend declares a variable that may use the += operator, @@ -192,40 +171,29 @@ func (reg *VarTypeRegistry) pkgloadlist( // that is sometimes composed of a common prefix and a package-specific // suffix. func (reg *VarTypeRegistry) pkgappend(varname string, basicType *BasicType) { - reg.acl(varname, basicType, - PackageSettable, - "buildlink3.mk, builtin.mk: none", - "Makefile, Makefile.*, *.mk: default, set, append, use") + reg.DefineName(varname, basicType, PackageSettable, "pkgappend") } func (reg *VarTypeRegistry) pkgappendbl3(varname string, basicType *BasicType) { - reg.acl(varname, basicType, - PackageSettable, - "Makefile, Makefile.*, *.mk: default, set, append, use") + reg.DefineName(varname, basicType, PackageSettable, "pkgappendbl3") } // Some package-defined variables may be modified in buildlink3.mk files. // These variables are typically related to compiling and linking files // from C and related languages. func (reg *VarTypeRegistry) pkgbl3(varname string, basicType *BasicType) { - reg.acl(varname, basicType, - PackageSettable, - "Makefile, Makefile.*, *.mk: default, set, use") + reg.DefineName(varname, basicType, PackageSettable, "pkgbl3") } // Some package-defined lists may also be modified in buildlink3.mk files, // for example platform-specific CFLAGS and LDFLAGS. func (reg *VarTypeRegistry) pkglistbl3(varname string, basicType *BasicType) { - reg.acl(varname, basicType, - List|PackageSettable, - "Makefile, Makefile.*, *.mk: default, set, append, use") + reg.DefineName(varname, basicType, List|PackageSettable, "pkglistbl3") } // Like pkglistbl3, but always needs a rationale. func (reg *VarTypeRegistry) pkglistbl3rat(varname string, basicType *BasicType) { - reg.acl(varname, basicType, - List|PackageSettable|NeedsRationale, - "Makefile, Makefile.*, *.mk: default, set, append, use") + reg.DefineName(varname, basicType, List|PackageSettable|NeedsRationale, "pkglistbl3") } // sys declares a user-defined or system-defined variable that must not @@ -238,41 +206,25 @@ func (reg *VarTypeRegistry) pkglistbl3ra // TODO: These timing issues should be handled separately from the permissions. // They can be made more precise. func (reg *VarTypeRegistry) sys(varname string, basicType *BasicType, options ...vartypeOptions) { - reg.acl(varname, basicType, - reg.options(SystemProvided, options), - "buildlink3.mk: none", - "*: use") + reg.DefineName(varname, basicType, reg.options(SystemProvided, options), "sys") } func (reg *VarTypeRegistry) sysbl3(varname string, basicType *BasicType) { - reg.acl(varname, basicType, - SystemProvided, - "*: use") + reg.DefineName(varname, basicType, SystemProvided, "sysbl3") } func (reg *VarTypeRegistry) syslist(varname string, basicType *BasicType) { - reg.acllist(varname, basicType, - List|SystemProvided, - "buildlink3.mk: none", - "*: use") + reg.DefineName(varname, basicType, List|SystemProvided, "syslist") } // usr declares a user-defined variable that must not be modified by packages. func (reg *VarTypeRegistry) usr(varname string, basicType *BasicType) { - reg.acl(varname, basicType, - // TODO: why is builtin.mk missing here? - UserSettable, - "buildlink3.mk: none", - "*: use, use-loadtime") + reg.DefineName(varname, basicType, UserSettable, "usr") } // usr declares a user-defined list variable that must not be modified by packages. func (reg *VarTypeRegistry) usrlist(varname string, basicType *BasicType) { - reg.acllist(varname, basicType, - // TODO: why is builtin.mk missing here? - List|UserSettable, - "buildlink3.mk: none", - "*: use, use-loadtime") + reg.DefineName(varname, basicType, List|UserSettable, "usrlist") } // A few variables from mk/defaults/mk.conf may be overridden by packages. @@ -286,52 +238,37 @@ func (reg *VarTypeRegistry) usrlist(varn // // TODO: parse all the below information directly from mk/defaults/mk.conf. func (reg *VarTypeRegistry) usrpkg(varname string, basicType *BasicType) { - reg.acl(varname, basicType, - PackageSettable|UserSettable, - "Makefile: default, set, use, use-loadtime", - "buildlink3.mk, builtin.mk: none", - "Makefile.*, *.mk: default, set, use, use-loadtime", - "*: use, use-loadtime") + reg.DefineName(varname, basicType, PackageSettable|UserSettable, "usrpkg") } // sysload declares a system-provided variable that may already be used at load time. // // TODO: For some of these variables, bsd.prefs.mk has to be included first. func (reg *VarTypeRegistry) sysload(varname string, basicType *BasicType, options ...vartypeOptions) { - reg.acl(varname, basicType, - reg.options(SystemProvided, options), - "*: use, use-loadtime") + reg.DefineName(varname, basicType, reg.options(SystemProvided, options), "sysload") } func (reg *VarTypeRegistry) sysloadlist(varname string, basicType *BasicType, options ...vartypeOptions) { - reg.acl(varname, basicType, - reg.options(List|SystemProvided, options), - "*: use, use-loadtime") + reg.DefineName(varname, basicType, reg.options(List|SystemProvided, options), "sysload") } // bl3list declares a list variable that is defined by buildlink3.mk and // builtin.mk and can later be used by the package. func (reg *VarTypeRegistry) bl3list(varname string, basicType *BasicType) { - reg.acl(varname, basicType, + reg.DefineName(varname, basicType, List, // not PackageSettable since the package uses it more than setting it. - "buildlink3.mk, builtin.mk: append", - "*: use") + "bl3list") } // cmdline declares a variable that is defined on the command line. There // are only few variables of this type, such as PKG_DEBUG_LEVEL. func (reg *VarTypeRegistry) cmdline(varname string, basicType *BasicType, options ...vartypeOptions) { - reg.acl(varname, basicType, - reg.options(CommandLineProvided, options), - "buildlink3.mk, builtin.mk: none", - "*: use, use-loadtime") + reg.DefineName(varname, basicType, reg.options(CommandLineProvided, options), "cmdline") } // Only for infrastructure files; see mk/misc/show.mk func (reg *VarTypeRegistry) infralist(varname string, basicType *BasicType) { - reg.acllist(varname, basicType, - List, - "*: set, append, use") + reg.DefineName(varname, basicType, List, "infralist") } // compilerLanguages reads the available languages that are typically @@ -490,10 +427,67 @@ func (reg *VarTypeRegistry) options(base return opts } +func (reg *VarTypeRegistry) compile(name string, aclEntries ...string) { + reg.cache[name] = reg.parseACLEntries(name, aclEntries...) +} + // 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. func (reg *VarTypeRegistry) Init(src *Pkgsrc) { + reg.compile("pkg", + "buildlink3.mk, builtin.mk: none", + "Makefile, Makefile.*, *.mk: default, set, use") + reg.compile("pkgload", + "buildlink3.mk: none", + "builtin.mk: use, use-loadtime", + "Makefile, Makefile.*, *.mk: default, set, use, use-loadtime") + reg.compile("pkglist", + "buildlink3.mk, builtin.mk: none", + "Makefile, Makefile.*, *.mk: default, set, append, use") + reg.compile("pkgloadlist", + "buildlink3.mk, builtin.mk: none", + "Makefile, Makefile.*, *.mk: default, set, append, use, use-loadtime") + reg.compile("pkgappend", + "buildlink3.mk, builtin.mk: none", + "Makefile, Makefile.*, *.mk: default, set, append, use") + reg.compile("pkgappendbl3", + "Makefile, Makefile.*, *.mk: default, set, append, use") + reg.compile("pkgbl3", + "Makefile, Makefile.*, *.mk: default, set, use") + reg.compile("pkglistbl3", + "Makefile, Makefile.*, *.mk: default, set, append, use") + reg.compile("sys", + "buildlink3.mk: none", + "*: use") + reg.compile("sysbl3", + "*: use") + reg.compile("syslist", + "buildlink3.mk: none", + "*: use") + reg.compile("usr", + // TODO: why is builtin.mk missing here? + "buildlink3.mk: none", + "*: use, use-loadtime") + reg.compile("usrlist", + // TODO: why is builtin.mk missing here? + "buildlink3.mk: none", + "*: use, use-loadtime") + reg.compile("usrpkg", + "Makefile: default, set, use, use-loadtime", + "buildlink3.mk, builtin.mk: none", + "Makefile.*, *.mk: default, set, use, use-loadtime", + "*: use, use-loadtime") + reg.compile("sysload", + "*: use, use-loadtime") + reg.compile("bl3list", + "buildlink3.mk, builtin.mk: append", + "*: use") + reg.compile("cmdline", + "buildlink3.mk, builtin.mk: none", + "*: use, use-loadtime") + reg.compile("infralist", + "*: set, append, use") compilers := reg.enumFrom(src, "mk/compiler.mk", Index: pkgsrc/pkgtools/pkglint/files/vardefs_test.go diff -u pkgsrc/pkgtools/pkglint/files/vardefs_test.go:1.27 pkgsrc/pkgtools/pkglint/files/vardefs_test.go:1.28 --- pkgsrc/pkgtools/pkglint/files/vardefs_test.go:1.27 Sun Dec 8 00:06:38 2019 +++ pkgsrc/pkgtools/pkglint/files/vardefs_test.go Wed Mar 18 08:24:49 2020 @@ -6,6 +6,7 @@ func (s *Suite) Test_VarTypeRegistry_acl t := s.Init(c) reg := NewVarTypeRegistry() + reg.compile("pkg", "*.mk: use") reg.pkg("VARNAME", BtUnknown) t.ExpectPanic( Index: pkgsrc/pkgtools/pkglint/files/vartypecheck.go diff -u pkgsrc/pkgtools/pkglint/files/vartypecheck.go:1.82 pkgsrc/pkgtools/pkglint/files/vartypecheck.go:1.83 --- pkgsrc/pkgtools/pkglint/files/vartypecheck.go:1.82 Sat Feb 15 13:48:40 2020 +++ pkgsrc/pkgtools/pkglint/files/vartypecheck.go Wed Mar 18 08:24:49 2020 @@ -1,6 +1,7 @@ package pkglint import ( + "netbsd.org/pkglint/pkgver" "netbsd.org/pkglint/regex" "netbsd.org/pkglint/textproc" "path" @@ -371,6 +372,59 @@ func (cv *VartypeCheck) DependencyPatter "To make the \"2.0\" above part of the package basename, the hyphen", "must be omitted, so the full package name becomes \"foo2.0-2.1.x\".") } + + checkBuildlinkApiDepends := func() { + if deppat.LowerOp == "" { + return + } + pkg := cv.MkLines.pkg + if pkg == nil { + return + } + if !hasPrefix(cv.Varname, "BUILDLINK_API_DEPENDS.") { + return + } + bl3id := Buildlink3ID(varnameParam(cv.Varname)) + data := pkg.bl3Data[bl3id] + if data == nil { + return + } + if data.apiDepends.LowerOp != deppat.LowerOp { + return + } + if pkgver.Compare(deppat.Lower, data.apiDepends.Lower) < 0 { + cv.Warnf("Version %s is smaller than the default version %s from %s.", + deppat.Lower, data.apiDepends.Lower, cv.MkLine.RelMkLine(data.apiDependsLine)) + } + } + + checkBuildlinkAbiDepends := func() { + if deppat.LowerOp == "" { + return + } + pkg := cv.MkLines.pkg + if pkg == nil { + return + } + if !hasPrefix(cv.Varname, "BUILDLINK_ABI_DEPENDS.") { + return + } + bl3id := Buildlink3ID(varnameParam(cv.Varname)) + data := pkg.bl3Data[bl3id] + if data == nil { + return + } + if data.abiDepends.LowerOp != deppat.LowerOp { + return + } + if pkgver.Compare(deppat.Lower, data.abiDepends.Lower) < 0 { + cv.Warnf("Version %s is smaller than the default version %s from %s.", + deppat.Lower, data.abiDepends.Lower, cv.MkLine.RelMkLine(data.abiDependsLine)) + } + } + + checkBuildlinkApiDepends() + checkBuildlinkAbiDepends() } func (cv *VartypeCheck) DependencyWithPath() { Index: pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go diff -u pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go:1.77 pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go:1.78 --- pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go:1.77 Sat Feb 15 13:48:40 2020 +++ pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go Wed Mar 18 08:24:49 2020 @@ -484,6 +484,29 @@ func (s *Suite) Test_VartypeCheck_Depend "WARN: filename.mk:67: Invalid dependency pattern \"${RUBY_PKGPREFIX}-theme-[a-z0-9]*\".") } +func (s *Suite) Test_VartypeCheck_DependencyPattern__smaller_version(c *check.C) { + t := s.Init(c) + + t.SetUpPackage("category/package", + ".include \"../../category/lib/buildlink3.mk\"", + "BUILDLINK_API_DEPENDS.lib+=\tlib>=1.0pkg", + "BUILDLINK_ABI_DEPENDS.lib+=\tlib>=1.1pkg") + t.SetUpPackage("category/lib") + t.CreateFileBuildlink3("category/lib/buildlink3.mk", + "BUILDLINK_API_DEPENDS.lib+=\tlib>=1.3api", + "BUILDLINK_ABI_DEPENDS.lib+=\tlib>=1.4abi") + t.Chdir("category/package") + t.FinishSetUp() + + G.checkdirPackage(".") + + t.CheckOutputLines( + "WARN: Makefile:21: Version 1.0pkg is smaller than the default "+ + "version 1.3api from ../../category/lib/buildlink3.mk:12.", + "WARN: Makefile:22: Version 1.1pkg is smaller than the default "+ + "version 1.4abi from ../../category/lib/buildlink3.mk:13.") +} + func (s *Suite) Test_VartypeCheck_DependencyWithPath(c *check.C) { t := s.Init(c) @@ -610,7 +633,7 @@ func (s *Suite) Test_VartypeCheck_EmulPl func (s *Suite) Test_VartypeCheck_Enum(c *check.C) { basicType := enum("jdk1 jdk2 jdk4") - G.Pkgsrc.vartypes.Define("JDK", basicType, UserSettable) + G.Pkgsrc.vartypes.Define("JDK", basicType, UserSettable, []ACLEntry{}) vt := NewVartypeCheckTester(s.Init(c), basicType) vt.Varname("JDK") @@ -1334,11 +1357,12 @@ func (s *Suite) Test_VartypeCheck_Pathna } func (s *Suite) Test_VartypeCheck_PathnameSpace(c *check.C) { + t := s.Init(c) // Invent a variable name since this data type is only used as part // of CONF_FILES. - G.Pkgsrc.vartypes.DefineParse("CONFIG_FILE", BtPathnameSpace, + t.SetUpType("CONFIG_FILE", BtPathnameSpace, NoVartypeOptions, "*.mk: set, use") - vt := NewVartypeCheckTester(s.Init(c), BtPathnameSpace) + vt := NewVartypeCheckTester(t, BtPathnameSpace) vt.Varname("CONFIG_FILE") vt.Values( --_----------=_158451989062240--