aboutsummaryrefslogtreecommitdiffstats
path: root/classes/external-toolchain.bbclass
blob: f1ffc25780aa26b00b88a22f611918233cbf20f6 (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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# This class provides everything necessary for a recipe to pull bits from an
# external toolchain:
# - Automatically sets LIC_FILES_CHKSUM based on LICENSE if appropriate
# - Searches the external toolchain sysroot and alternate locations for the
#   patterns specified in the FILES variables, with support for checking
#   alternate locations within the sysroot as well
# - Automatically PROVIDES/RPROVIDES the non-external-suffixed names
# - Usual bits to handle packaging of existing binaries
# - Automatically skips the recipe if its files aren't available in the
#   external toolchain

# Since these are prebuilt binaries, there are no source files to checksum for
# LIC_FILES_CHKSUM, so use the license from common-licenses
inherit common-license external-debugsrc

OVERRIDES =. "external:"

# Prebuilt binaries, no need for any default dependencies
INHIBIT_DEFAULT_DEPS = "1"

# Missing build deps don't matter when we don't build anything
INSANE_SKIP:${PN} += "build-deps"

EXTERNAL_PN ?= "${@PN.replace('-external', '')}"
PROVIDES += "${EXTERNAL_PN}"
LICENSE = "CLOSED"
LIC_FILES_CHKSUM = "${COMMON_LIC_CHKSUM}"

# Packaging requires objcopy/etc for split and strip
PACKAGE_DEPENDS += "virtual/${MLPREFIX}${TARGET_PREFIX}binutils"

do_configure[noexec] = "1"
do_compile[noexec] = "1"

EXTERNAL_PV_PREFIX ?= ""
EXTERNAL_PV_SUFFIX ?= ""
PV:prepend = "${@'${EXTERNAL_PV_PREFIX}' if '${EXTERNAL_PV_PREFIX}' else ''}"
PV:append = "${@'${EXTERNAL_PV_SUFFIX}' if '${EXTERNAL_PV_SUFFIX}' else ''}"

EXTERNAL_EXTRA_FILES ?= ""

# Skip this recipe if we don't have files in the external toolchain
EXTERNAL_AUTO_PROVIDE ?= "0"
EXTERNAL_AUTO_PROVIDE:class-target ?= "1"

# We don't care if this path references other variables
EXTERNAL_TOOLCHAIN[vardepvalue] = "${EXTERNAL_TOOLCHAIN}"

# We don't want to rebuild if the path to the toolchain changes, only if the
# toolchain changes
external_toolchain_do_install[vardepsexclude] += "EXTERNAL_TOOLCHAIN"
EXTERNAL_INSTALL_SOURCE_PATHS[vardepsexclude] += "EXTERNAL_TOOLCHAIN"

# Propagate file permissions mode from owner to the rest of the user
# so the toolchain bits would be available for everyone to use even in the
# directories with root permissions.
EXTERNAL_PROPAGATE_MODE ?= "0"

python () {
    # Skipping only matters up front
    if d.getVar('BB_WORKERCONTEXT', True) == '1':
        return

    if not d.getVar('TCMODE', True).startswith('external'):
        raise bb.parse.SkipPackage("External toolchain not configured (TCMODE not set to an external toolchain).")

    # We're not an available provider if there's no external toolchain
    if not d.getVar("EXTERNAL_TOOLCHAIN", True):
        raise bb.parse.SkipPackage("External toolchain not configured (EXTERNAL_TOOLCHAIN not set).")

    if not bb.utils.to_boolean(d.getVar('EXTERNAL_AUTO_PROVIDE', d)):
        return

    sysroots, mirrors, premirrors = oe.external.get_file_search_metadata(d)
    search_patterns = []
    pattern = d.getVar('EXTERNAL_PROVIDE_PATTERN', True)
    if pattern:
        search_patterns.append(pattern)
    else:
        files = oe.external.gather_pkg_files(d)
        search_patterns.extend(filter(lambda f: '.debug' not in f, files))

    expanded = oe.external.expand_paths(search_patterns, mirrors, premirrors)
    paths = oe.external.search_sysroots(expanded, sysroots)
    if not any(f for p, f in paths):
        raise bb.parse.SkipPackage('No files found in external toolchain sysroot for: {}'.format(', '.join(search_patterns)))
}

fakeroot python do_install () {
    bb.build.exec_func('external_toolchain_do_install', d)
    pass # Sentinel
}

def external_toolchain_propagate_mode (d, installdest):
    import stat

    propagate = d.getVar('EXTERNAL_PROPAGATE_MODE', True)
    if propagate == '0':
        return

    for root, dirs, files in os.walk(installdest):
        for bit in dirs + files:
            path = os.path.join(root, bit)
            try:
                bitstat = os.stat(path)
            except ValueError:
                continue
            else:
                newmode = bitstat.st_mode
                if bitstat.st_mode & stat.S_IRUSR:
                    newmode |= stat.S_IRGRP | stat.S_IROTH
                if bitstat.st_mode & stat.S_IXUSR:
                    newmode |= stat.S_IXGRP | stat.S_IXOTH
                os.chmod(path, newmode)

python external_toolchain_do_install () {
    import subprocess
    installdest = d.getVar('D', True)
    sysroots, mirrors, premirrors = oe.external.get_file_search_metadata(d)
    files = oe.external.gather_pkg_files(d)
    oe.external.copy_from_sysroots(files, sysroots, mirrors, premirrors, installdest)
    if 'do_install_extra' in d:
        bb.build.exec_func('do_install_extra', d)
    external_toolchain_propagate_mode(d, installdest)
}
external_toolchain_do_install[vardeps] += "${@' '.join('FILES:%s' % pkg for pkg in '${PACKAGES}'.split())}"

# Change do_install's CWD to EXTERNAL_TOOLCHAIN for convenience
do_install[dirs] = "${D} ${EXTERNAL_TOOLCHAIN}"

python () {
    # Deal with any do_install:append
    install = d.getVar('do_install', False)
    try:
        base, appended = install.split('# Sentinel', 1)
    except ValueError:
        pass
    else:
        d.setVar('do_install', base)
        if appended.strip():
            d.setVar('do_install_added', appended)
            d.setVarFlag('do_install_added', 'func', '1')
            d.appendVarFlag('do_install', 'postfuncs', ' do_install_added')
}

# Toolchain shipped binaries weren't necessarily built ideally
WARN_QA:remove = "ldflags textrel"
ERROR_QA:remove = "ldflags textrel"

# Debug files may well have already been split out, or stripped out
INSANE_SKIP:${PN} += "already-stripped"

RPROVIDES:${PN} += "${EXTERNAL_PN}"
RPROVIDES:${PN}-dev += "${EXTERNAL_PN}-dev"
RPROVIDES:${PN}-staticdev += "${EXTERNAL_PN}-staticdev"
RPROVIDES:${PN}-dbg += "${EXTERNAL_PN}-dbg"
RPROVIDES:${PN}-doc += "${EXTERNAL_PN}-doc"
RPROVIDES:${PN}-locale += "${EXTERNAL_PN}-locale"
LOCALEBASEPN = "${EXTERNAL_PN}"

FILES:${PN} = ""
FILES:${PN}-dev = ""
FILES:${PN}-staticdev = ""
FILES:${PN}-doc = ""
FILES:${PN}-locale = ""

def debug_paths(d):
    l = d.createCopy()
    paths = []
    exclude = [
        l.getVar('datadir', True),
        l.getVar('includedir', True),
    ]
    for p in l.getVar('PACKAGES', True).split():
        if p.endswith('-dbg'):
            continue
        for f in (l.getVar('FILES:%s' % p, True) or '').split():
            if any((f == x or f.startswith(x + '/')) for x in exclude):
                continue
            d = os.path.dirname(f)
            b = os.path.basename(f)
            paths.append('/usr/lib/debug{0}/{1}.debug'.format(d, b))
            paths.append('{0}/.debug/{1}'.format(d, b))
            paths.append('{0}/.debug/{1}.debug'.format(d, b))
    return set(paths)

FILES:${PN}-dbg = "${@' '.join(debug_paths(d))}"