summaryrefslogtreecommitdiffstats
path: root/scripts/lib/recipetool/create_kmod.py
blob: 4569b53c805185ed352eaf1974061c38f82d2b21 (plain)
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
# Recipe creation tool - kernel module support plugin
#
# Copyright (C) 2016 Intel Corporation
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import re
import logging
from recipetool.create import RecipeHandler, read_pkgconfig_provides, validate_pv

logger = logging.getLogger('recipetool')

tinfoil = None

def tinfoil_init(instance):
    global tinfoil
    tinfoil = instance


class KernelModuleRecipeHandler(RecipeHandler):
    def process(self, srctree, classes, lines_before, lines_after, handled, extravalues):
        import bb.process
        if 'buildsystem' in handled:
            return False

        module_inc_re = re.compile(r'^#include\s+<linux/module.h>$')
        makefiles = []
        is_module = False

        makefiles = []

        files = RecipeHandler.checkfiles(srctree, ['*.c', '*.h'], recursive=True, excludedirs=['contrib', 'test', 'examples'])
        if files:
            for cfile in files:
                # Look in same dir or parent for Makefile
                for makefile in [os.path.join(os.path.dirname(cfile), 'Makefile'), os.path.join(os.path.dirname(os.path.dirname(cfile)), 'Makefile')]:
                    if makefile in makefiles:
                        break
                    else:
                        if os.path.exists(makefile):
                            makefiles.append(makefile)
                            break
                else:
                    continue
                with open(cfile, 'r', errors='surrogateescape') as f:
                    for line in f:
                        if module_inc_re.match(line.strip()):
                            is_module = True
                            break
                if is_module:
                    break

        if is_module:
            classes.append('module')
            handled.append('buildsystem')
            # module.bbclass and the classes it inherits do most of the hard
            # work, but we need to tweak it slightly depending on what the
            # Makefile does (and there is a range of those)
            # Check the makefile for the appropriate install target
            install_lines = []
            compile_lines = []
            in_install = False
            in_compile = False
            install_target = None
            with open(makefile, 'r', errors='surrogateescape') as f:
                for line in f:
                    if line.startswith('install:'):
                        if not install_lines:
                            in_install = True
                            install_target = 'install'
                    elif line.startswith('modules_install:'):
                        install_lines = []
                        in_install = True
                        install_target = 'modules_install'
                    elif line.startswith('modules:'):
                        compile_lines = []
                        in_compile = True
                    elif line.startswith(('all:', 'default:')):
                        if not compile_lines:
                            in_compile = True
                    elif line:
                        if line[0] == '\t':
                            if in_install:
                                install_lines.append(line)
                            elif in_compile:
                                compile_lines.append(line)
                        elif ':' in line:
                            in_install = False
                            in_compile = False

            def check_target(lines, install):
                kdirpath = ''
                manual_install = False
                for line in lines:
                    splitline = line.split()
                    if splitline[0] in ['make', 'gmake', '$(MAKE)']:
                        if '-C' in splitline:
                            idx = splitline.index('-C') + 1
                            if idx < len(splitline):
                                kdirpath = splitline[idx]
                                break
                    elif install and splitline[0] == 'install':
                        if '.ko' in line:
                            manual_install = True
                return kdirpath, manual_install

            kdirpath = None
            manual_install = False
            if install_lines:
                kdirpath, manual_install = check_target(install_lines, install=True)
            if compile_lines and not kdirpath:
                kdirpath, _ = check_target(compile_lines, install=False)

            if manual_install or not install_lines:
                lines_after.append('EXTRA_OEMAKE_append_task-install = " -C ${STAGING_KERNEL_DIR} M=${S}"')
            elif install_target and install_target != 'modules_install':
                lines_after.append('MODULES_INSTALL_TARGET = "install"')

            warnmsg = None
            kdirvar = None
            if kdirpath:
                res = re.match(r'\$\(([^$)]+)\)', kdirpath)
                if res:
                    kdirvar = res.group(1)
                    if kdirvar != 'KERNEL_SRC':
                        lines_after.append('EXTRA_OEMAKE += "%s=${STAGING_KERNEL_DIR}"' % kdirvar)
                elif kdirpath.startswith('/lib/'):
                    warnmsg = 'Kernel path in install makefile is hardcoded - you will need to patch the makefile'
            if not kdirvar and not warnmsg:
                warnmsg = 'Unable to find means of passing kernel path into install makefile - if kernel path is hardcoded you will need to patch the makefile'
            if warnmsg:
                warnmsg += '. Note that the variable KERNEL_SRC will be passed in as the kernel source path.'
                logger.warn(warnmsg)
                lines_after.append('# %s' % warnmsg)

            return True

        return False

def register_recipe_handlers(handlers):
    handlers.append((KernelModuleRecipeHandler(), 15))