Search code examples
cross-compilingyoctobitbakeopenembedded

Yocto: Creating a New Cross Compiler for use by Other Recipes


Question

What is the appropriate methodology for creating a new, non-gcc cross-compiler for use by other recipes during build time?

NOTE: This is specifically about a cross compiler for use during build time, NOT for use with the SDK created by the populate_sdk task. Additional information about SDK generation is welcome but not the focus of the question.

Background

I am trying to integrate a proprietary software framework into a yocto build. For legal reasons (NDA) I can't talk about the specifics of the framework, however I expect this process will be the same for any other proprietary tool which produces code for another architecture. In true programmer fashion, let's call this framework foo. Imaginative, I know.

Thus far, I have created a recipe to build the cross-compiler itself (trimmed for brevity and NDA compliance):

foo-cross.bb

inherit cross

DEPENDS = ""

do_configure () { ... }

do_compile () { ... }

do_install () {
    install -d ${D}${libdir}/foo
    cp -r ./outdir/* ${D}${libdir}/foo
}

This recipe works, in that I can cd into the build directory and manually run the binaries to do things as expected. Hooray!

Next, I created a new BitBake class for applications which depend on this framework and attendant compiler (trimmed as before):

foo.bbclass

DEPENDS += "foo-cross"

# Do not inherit GCC and libc; this is handled by foo-cross
INHIBIT_DEFAULT_DEPS = "1"

do_compile() {
    ...
}
do_compile[depends] += "foo-cross:do_populate_sysroot"

fakeroot do_install { ... }
do_install[depends] += "virtual/fakeroot-native:do_populate_sysroot"

With this, any recipe should be able to inherit foo and be off to the races.

Problem

The primary issue is that using this scheme causes recipe-sysroot-native to not be populated with the installed contents of foo-cross, obviously causing compile failures. I do see testapp/1.0-r0/recipe-sysroot-native/installeddeps/foo-cross created but still nothing in recipe-sysroot-native/${libdir}/foo.

In a related issue, I get a warning message which I believe is the real root of the issues I am seeing:

WARNING: testapp-1.0-r0 do_prepare_recipe_sysroot: Manifest /build/build/tmp/sstate-control/manifest-x86_64_x86_64-nativesdk-foo-cross.populate_sysroot not found in raspberrypi4 armv7vet2hf-neon-vfpv4 armv7vehf-neon-vfpv4 armv7vet2hf-neon armv7vehf-neon armv7vet2hf-vfp armv7vehf-vfp armv7at2hf-vfp armv7ahf-vfp armv6thf-vfp armv6hf-vfp armv5tehf-vfp armv5ehf-vfp armv5thf-vfp armv5hf-vfp allarch x86_64_x86_64-nativesdk (variant '')?

This is particularly baffling to me but explains (to some degree) why the files are missing. This raises a number of questions:

  1. Why is the manifest declared as x86_64_x86_64-nativesdk given that my host is x86_64 (the first portion) but my build target is an ARM-based platform?
  2. Where did the -nativesdk portion come from given that I am only inheriting cross?
  3. x86_64_x86_64-nativesdk is in the list of targets, why isn't it found?

Rather than list all of the things I have tried to address this error (been working on this for longer than I would care to admit), I return to my question from the top: What is the correct way to set up a new, non-gcc cross compiler for use with Yocto?


Solution

  • TL;DR

    Add the following to foo-cross.bb:

    PN = "foo-cross-${TUNE_PKGARCH}"
    PROVIDES = "foo-cross"
    

    Wait, what?

    Yocto uses a number of sources of information when determining exactly how a given recipe behaves. This includes obvious things such as include or inherit directives, explicit variable settings and the exact format of the recipe name itself (${PN}).

    It is this last condition that was my issue. You can find several places in the Yocto documentation where they mention specific recipe naming, for instance:

    When creating a recipe this way, the recipe name must follow this naming >convention:

    myrecipe-native.bb
                   
    

    Not using this naming convention can lead to subtle problems caused by existing code that depends on that naming convention.

    From The Yocto Mega Manual.

    Strangely, this pearl of wisdom is not mentioned in the documentation for either cross.bbclass nor staging.bbclass. If you were to look at, say, Poky's gcc-cross.bb or go-cross.bb recipes you might suspect that it is sufficient to simply add -cross and call it a day. You would be wrong. To see why, consider the following snippet from staging.bbclass (the actual file; not the documentation):

    native = False
    if c.endswith("-native") or "-cross-" in c or "-crosssdk" in c:
        native = True
    

    Do you see it? Look again. The identifier used to match cross toolchain recipes is -cross-. Note the trailing hyphen. If you assumed as I did above you will have a cross compiler recipe which inherits cross but which does not get declared as a native recipe during staging. Boo.

    Fortunately this is solvable with the TL;DR above. First, we redefine ${PN} to be foo-cross-${TUNE_ARCH}. This causes the above case and another (buried in poky/meta/lib/oe/sstatesig.py) to resolve correctly. Second, we define PROVIDES so that we can still depend on foo-cross rather than the fully qualified name which, in my case, is very long.

    Notes

    1. The above analysis was performed on Yocto 3.1 and may change in future revisions.
    2. If a given recipe is only buildable for certain architectures it can still depend on foo-cross-longnamehere, as appropriate for the use-case.
    3. If you stare at the sources for staging.bbclass long enough, you can see a sailboat.