1def __note(msg, d):
2    bb.note("%s: recipe_sanity: %s" % (d.getVar("P"), msg))
3
4__recipe_sanity_badruntimevars = "RDEPENDS RPROVIDES RRECOMMENDS RCONFLICTS"
5def bad_runtime_vars(cfgdata, d):
6    if bb.data.inherits_class("native", d) or \
7            bb.data.inherits_class("cross", d):
8        return
9
10    for var in d.getVar("__recipe_sanity_badruntimevars").split():
11        val = d.getVar(var, False)
12        if val and val != cfgdata.get(var):
13            __note("%s should be %s_${PN}" % (var, var), d)
14
15__recipe_sanity_reqvars = "DESCRIPTION"
16__recipe_sanity_reqdiffvars = ""
17def req_vars(cfgdata, d):
18    for var in d.getVar("__recipe_sanity_reqvars").split():
19        if not d.getVar(var, False):
20            __note("%s should be set" % var, d)
21
22    for var in d.getVar("__recipe_sanity_reqdiffvars").split():
23        val = d.getVar(var, False)
24        cfgval = cfgdata.get(var)
25
26        if not val:
27            __note("%s should be set" % var, d)
28        elif val == cfgval:
29            __note("%s should be defined to something other than default (%s)" % (var, cfgval), d)
30
31def var_renames_overwrite(cfgdata, d):
32    renames = d.getVar("__recipe_sanity_renames", False)
33    if renames:
34        for (key, newkey, oldvalue, newvalue) in renames:
35            if oldvalue != newvalue and oldvalue != cfgdata.get(newkey):
36                __note("rename of variable '%s' to '%s' overwrote existing value '%s' with '%s'." % (key, newkey, oldvalue, newvalue), d)
37
38def incorrect_nonempty_PACKAGES(cfgdata, d):
39    if bb.data.inherits_class("native", d) or \
40            bb.data.inherits_class("cross", d):
41        if d.getVar("PACKAGES"):
42            return True
43
44def can_use_autotools_base(cfgdata, d):
45    cfg = d.getVar("do_configure")
46    if not bb.data.inherits_class("autotools", d):
47        return False
48
49    for i in ["autoreconf"] + ["%s_do_configure" % cls for cls in ["gnomebase", "gnome", "e", "autotools", "efl", "gpephone", "openmoko", "openmoko2", "xfce", "xlibs"]]:
50        if cfg.find(i) != -1:
51            return False
52
53    for clsfile in d.getVar("__inherit_cache", False):
54        (base, _) = os.path.splitext(os.path.basename(clsfile))
55        if cfg.find("%s_do_configure" % base) != -1:
56            __note("autotools_base usage needs verification, spotted %s_do_configure" % base, d)
57
58    return True
59
60def can_delete_FILESPATH(cfgdata, d):
61    expected = cfgdata.get("FILESPATH")
62    expectedpaths = d.expand(expected)
63    unexpanded = d.getVar("FILESPATH", False)
64    filespath = d.getVar("FILESPATH").split(":")
65    filespath = [os.path.normpath(f) for f in filespath if os.path.exists(f)]
66    for fp in filespath:
67        if not fp in expectedpaths:
68            # __note("Path %s in FILESPATH not in the expected paths %s" %
69            # (fp, expectedpaths), d)
70            return False
71    return expected != unexpanded
72
73def can_delete_others(p, cfgdata, d):
74    for k in ["S", "PV", "PN", "DESCRIPTION", "DEPENDS",
75              "SECTION", "PACKAGES", "EXTRA_OECONF", "EXTRA_OEMAKE"]:
76    #for k in cfgdata:
77        unexpanded = d.getVar(k, False)
78        cfgunexpanded = cfgdata.get(k)
79        if not cfgunexpanded:
80            continue
81
82        try:
83            expanded = d.getVar(k)
84            cfgexpanded = d.expand(cfgunexpanded)
85        except bb.fetch.ParameterError:
86            continue
87
88        if unexpanded != cfgunexpanded and \
89           cfgexpanded == expanded:
90           __note("candidate for removal of %s" % k, d)
91           bb.debug(1, "%s: recipe_sanity:   cfg's '%s' and d's '%s' both expand to %s" %
92                       (p, cfgunexpanded, unexpanded, expanded))
93
94python do_recipe_sanity () {
95    p = d.getVar("P")
96    p = "%s %s %s" % (d.getVar("PN"), d.getVar("PV"), d.getVar("PR"))
97
98    sanitychecks = [
99        (can_delete_FILESPATH, "candidate for removal of FILESPATH"),
100        #(can_use_autotools_base, "candidate for use of autotools_base"),
101        (incorrect_nonempty_PACKAGES, "native or cross recipe with non-empty PACKAGES"),
102    ]
103    cfgdata = d.getVar("__recipe_sanity_cfgdata", False)
104
105    for (func, msg) in sanitychecks:
106        if func(cfgdata, d):
107            __note(msg, d)
108
109    can_delete_others(p, cfgdata, d)
110    var_renames_overwrite(cfgdata, d)
111    req_vars(cfgdata, d)
112    bad_runtime_vars(cfgdata, d)
113}
114do_recipe_sanity[nostamp] = "1"
115addtask recipe_sanity
116
117do_recipe_sanity_all[nostamp] = "1"
118do_recipe_sanity_all[recrdeptask] = "do_recipe_sanity_all do_recipe_sanity"
119do_recipe_sanity_all () {
120    :
121}
122addtask recipe_sanity_all after do_recipe_sanity
123
124python recipe_sanity_eh () {
125    d = e.data
126
127    cfgdata = {}
128    for k in d.keys():
129        if not isinstance(d.getVar(k, False), bb.data_smart.DataSmart):
130            cfgdata[k] = d.getVar(k, False)
131
132    d.setVar("__recipe_sanity_cfgdata", cfgdata)
133    #d.setVar("__recipe_sanity_cfgdata", d)
134
135    # Sick, very sick..
136    from bb.data_smart import DataSmart
137    old = DataSmart.renameVar
138    def myrename(self, key, newkey):
139        oldvalue = self.getVar(newkey, 0)
140        old(self, key, newkey)
141        newvalue = self.getVar(newkey, 0)
142        if oldvalue:
143            renames = self.getVar("__recipe_sanity_renames", 0) or set()
144            renames.add((key, newkey, oldvalue, newvalue))
145            self.setVar("__recipe_sanity_renames", renames)
146    DataSmart.renameVar = myrename
147}
148addhandler recipe_sanity_eh
149recipe_sanity_eh[eventmask] = "bb.event.ConfigParsed"
150