Patchwork package: let dependency on a package name to be architecture specific

login
register
mail settings
Submitter Ming Liu
Date March 5, 2014, 10:08 a.m.
Message ID <1394014083-9969-1-git-send-email-ming.liu@windriver.com>
Download mbox | patch
Permalink /patch/68031/
State New
Headers show

Comments

Ming Liu - March 5, 2014, 10:08 a.m.
For multilib builds that rpm is the first package backend, it would be
often desireable to express that a package of compatible architecture
is needed to satisfy a dependency. In most of the cases this is already
handled by the automatically extracted soname dependencies, but this is
not always the case: sometimes it's necessary to disable the automatic
dependency generation, and then there are cases where the information
cannot be automatically generated, such as: -dev package dependencies
on other -dev packages or plugins are dynamically loaded via dlopen,
leading to obscure build failure that a 32bit package would incorrectly
satisfy the dependency for a 64bit package and similarly vice versa.

The patch mainly aims to resolve this problem, the basic idea is adding
a ARCH_SPECIFIC variable for packages satisfying following scenarios:
  * when rpm is being set as the first package backend
  * it's not a allarch package
  * it only provides libs without bins meanwhile

Every satisfied package would be set with
"ARCH_SPECIFIC_pkgname = 1:${TARGET_ARCH}" and it's gonna be written into
pkgdata if it exists, to mark the package as architecture specific, and
it will be checked during runtime_mapping_rename, all architecture
specific dependencies would be expanded with a ${TARGET_ARCH} suffix,
that declares a dependency on a package name architecture specific and
permits differentiating between 32-bit and 64-bit versions.

Off course, the user could set "ARCH_SPECIFIC_pkgname = 0" in recipes to
disable this feature.

Signed-off-by: Ming Liu <ming.liu@windriver.com>
---
 meta/classes/image.bbclass             |    6 +-
 meta/classes/package.bbclass           |   78 ++++++++++++++++++++++++++------
 meta/classes/populate_sdk_base.bbclass |    2 +-
 3 files changed, 68 insertions(+), 18 deletions(-)
Richard Purdie - March 7, 2014, 7:26 a.m.
On Wed, 2014-03-05 at 18:08 +0800, Ming Liu wrote:
> For multilib builds that rpm is the first package backend, it would be
> often desireable to express that a package of compatible architecture
> is needed to satisfy a dependency. In most of the cases this is already
> handled by the automatically extracted soname dependencies, but this is
> not always the case: sometimes it's necessary to disable the automatic
> dependency generation, and then there are cases where the information
> cannot be automatically generated, such as: -dev package dependencies
> on other -dev packages or plugins are dynamically loaded via dlopen,
> leading to obscure build failure that a 32bit package would incorrectly
> satisfy the dependency for a 64bit package and similarly vice versa.
> 
> The patch mainly aims to resolve this problem, the basic idea is adding
> a ARCH_SPECIFIC variable for packages satisfying following scenarios:
>   * when rpm is being set as the first package backend

I don't like this idea at all. It shouldn't matter whether rpm is set
first or last, it should behave the same.

I'm also rather dubious about this patch in general. It seems wrong to
have to put this into generic code for what amounts to an rpm backend
issue.

Finally, this patch causes build failures in do_package_write_deb:

eg:

http://autobuilder.yoctoproject.org/main/builders/build-appliance/builds/61/steps/BuildImages_1/logs/stdio
http://autobuilder.yoctoproject.org/main/builders/nightly-arm/builds/82/steps/BuildImages/logs/stdio

and many more. I'm trying to get the tree stablised and having build
failures like this is extremely frustrating as once again I'm blocked on
merging patches until I remove this patch and run another build :(

Cheers,

Richard 



>   * it's not a allarch package
>   * it only provides libs without bins meanwhile
> 
> Every satisfied package would be set with
> "ARCH_SPECIFIC_pkgname = 1:${TARGET_ARCH}" and it's gonna be written into
> pkgdata if it exists, to mark the package as architecture specific, and
> it will be checked during runtime_mapping_rename, all architecture
> specific dependencies would be expanded with a ${TARGET_ARCH} suffix,
> that declares a dependency on a package name architecture specific and
> permits differentiating between 32-bit and 64-bit versions.
> 
> Off course, the user could set "ARCH_SPECIFIC_pkgname = 0" in recipes to
> disable this feature.
> 
> Signed-off-by: Ming Liu <ming.liu@windriver.com>
> ---
>  meta/classes/image.bbclass             |    6 +-
>  meta/classes/package.bbclass           |   78 ++++++++++++++++++++++++++------
>  meta/classes/populate_sdk_base.bbclass |    2 +-
>  3 files changed, 68 insertions(+), 18 deletions(-)
> 
> diff --git a/meta/classes/image.bbclass b/meta/classes/image.bbclass
> index 28b68f9..670f173 100644
> --- a/meta/classes/image.bbclass
> +++ b/meta/classes/image.bbclass
> @@ -225,9 +225,9 @@ do_rootfs[prefuncs] += "rootfs_process_ignore"
>  # may have occurred.
>  python rootfs_runtime_mapping() {
>      pn = d.getVar('PN', True)
> -    runtime_mapping_rename("PACKAGE_INSTALL", pn, d)
> -    runtime_mapping_rename("PACKAGE_INSTALL_ATTEMPTONLY", pn, d)
> -    runtime_mapping_rename("BAD_RECOMMENDATIONS", pn, d)
> +    runtime_mapping_rename("PACKAGE_INSTALL", pn, False, d)
> +    runtime_mapping_rename("PACKAGE_INSTALL_ATTEMPTONLY", pn, False, d)
> +    runtime_mapping_rename("BAD_RECOMMENDATIONS", pn, False, d)
>  }
>  do_rootfs[prefuncs] += "rootfs_runtime_mapping"
>  
> diff --git a/meta/classes/package.bbclass b/meta/classes/package.bbclass
> index 0018a62..3e0ca30 100644
> --- a/meta/classes/package.bbclass
> +++ b/meta/classes/package.bbclass
> @@ -346,19 +346,29 @@ def copydebugsources(debugsrcdir, d):
>  # Package data handling routines
>  #
>  
> -def get_package_mapping (pkg, basepkg, d):
> +def get_package_mapping (pkg, basepkg, check_arch, d):
>      import oe.packagedata
>  
> +    ret = pkg
>      data = oe.packagedata.read_subpkgdata(pkg, d)
> -    key = "PKG_%s" % pkg
> +    pkg_key = "PKG_%s" % pkg
> +    arch_key = "ARCH_SPECIFIC_%s" % pkg
> +    arch_specific = '0'
>  
> -    if key in data:
> +    if pkg_key in data:
>          # Have to avoid undoing the write_extra_pkgs(global_variants...)
> -        if bb.data.inherits_class('allarch', d) and data[key] == basepkg:
> +        if bb.data.inherits_class('allarch', d) and data[pkg_key] == basepkg:
>              return pkg
> -        return data[key]
> +        ret = data[pkg_key]
>  
> -    return pkg
> +    if arch_key in data:
> +        arch_specific = data[arch_key]
> +
> +    items = arch_specific.split(':')
> +    if len(items) > 1 and items[0] == '1' and check_arch == True:
> +        ret += '(' + items[1] + ')'
> +
> +    return ret
>  
>  def get_package_additional_metadata (pkg_type, d):
>      base_key = "PACKAGE_ADD_METADATA"
> @@ -371,13 +381,13 @@ def get_package_additional_metadata (pkg_type, d):
>          metadata_fields = [field.strip() for field in oe.data.typed_value(key, d)]
>          return "\n".join(metadata_fields).strip()
>  
> -def runtime_mapping_rename (varname, pkg, d):
> +def runtime_mapping_rename (varname, pkg, check_arch, d):
>      #bb.note("%s before: %s" % (varname, d.getVar(varname, True)))
>  
>      new_depends = {}
>      deps = bb.utils.explode_dep_versions2(d.getVar(varname, True) or "")
>      for depend in deps:
> -        new_depend = get_package_mapping(depend, pkg, d)
> +        new_depend = get_package_mapping(depend, pkg, check_arch, d)
>          new_depends[new_depend] = deps[depend]
>  
>      d.setVar(varname, bb.utils.join_deps(new_depends, commasep=False))
> @@ -1119,6 +1129,34 @@ python package_fixsymlinks () {
>          d.setVar('RDEPENDS_' + pkg, bb.utils.join_deps(rdepends, commasep=False))
>  }
>  
> +python package_check_arch () {
> +    import glob, re
> +
> +    def check_arch_specific(pkg):
> +        bin_re = re.compile(".*/s?" + os.path.basename(d.getVar("bindir", True)) + "$")
> +        lib_re = re.compile(".*/" + os.path.basename(d.getVar("libdir", True)) + "($|/.*$)")
> +        has_bins = 0
> +        has_libs = 0
> +
> +        for file in pkgfiles[pkg]:
> +            root = os.path.dirname(file)
> +            if bin_re.match(root):
> +                has_bins = 1
> +            if lib_re.match(root):
> +                has_libs = 1
> +
> +        if not bb.data.inherits_class('allarch', d) and not has_bins and has_libs:
> +            if not d.getVar('ARCH_SPECIFIC_' + pkg):
> +                d.setVar('ARCH_SPECIFIC_' + pkg, "%s:%s" % ('1', d.getVar('TARGET_ARCH', True)))
> +
> +    # Do nothing if rpm is not the first package backend
> +    if d.getVar('PACKAGE_CLASSES', True).split()[0] != "package_rpm":
> +        return
> +
> +    for pkg in sorted((d.getVar('PACKAGES', True) or "").split()):
> +        check_arch_specific(pkg)
> +}
> +
>  PKGDESTWORK = "${WORKDIR}/pkgdata"
>  
>  python emit_pkgdata() {
> @@ -1184,6 +1222,16 @@ python emit_pkgdata() {
>              pkgval = pkg
>              d.setVar('PKG_%s' % pkg, pkg)
>  
> +        arch_specific = d.getVar('ARCH_SPECIFIC_%s' % pkg, True) or ""
> +        items = arch_specific.split(':')
> +        if len(items) > 1 and items[0] == '1':
> +            rprovides = d.getVar('RPROVIDES_%s' % pkg, True)
> +            if rprovides == None:
> +                rprovides = pkg + ' (=' + d.getVar('EXTENDPKGV', True) + ')'
> +            else:
> +                rprovides += ' ' + pkg + ' (=' + d.getVar('EXTENDPKGV', True) + ')'
> +            d.setVar('RPROVIDES_%s' % pkg, rprovides)
> +
>          pkgdestpkg = os.path.join(pkgdest, pkg)
>          files = {}
>          total_size = 0
> @@ -1215,6 +1263,7 @@ python emit_pkgdata() {
>          write_if_exists(sf, pkg, 'SECTION')
>          write_if_exists(sf, pkg, 'PKG')
>          write_if_exists(sf, pkg, 'ALLOW_EMPTY')
> +        write_if_exists(sf, pkg, 'ARCH_SPECIFIC')
>          write_if_exists(sf, pkg, 'FILES')
>          write_if_exists(sf, pkg, 'pkg_postinst')
>          write_if_exists(sf, pkg, 'pkg_postrm')
> @@ -1878,6 +1927,7 @@ PACKAGESPLITFUNCS ?= " \
>  PACKAGEFUNCS += " \
>                  package_fixsymlinks \
>                  package_name_hook \
> +                package_check_arch \
>                  package_do_filedeps \
>                  package_do_shlibs \
>                  package_do_pkgconfig \
> @@ -2010,10 +2060,10 @@ def mapping_rename_hook(d):
>      like debian.bbclass or manual PKG variable name changes
>      """
>      pkg = d.getVar("PKG", True)
> -    runtime_mapping_rename("RDEPENDS", pkg, d)
> -    runtime_mapping_rename("RRECOMMENDS", pkg, d)
> -    runtime_mapping_rename("RSUGGESTS", pkg, d)
> -    runtime_mapping_rename("RPROVIDES", pkg, d)
> -    runtime_mapping_rename("RREPLACES", pkg, d)
> -    runtime_mapping_rename("RCONFLICTS", pkg, d)
> +    runtime_mapping_rename("RDEPENDS", pkg, True, d)
> +    runtime_mapping_rename("RRECOMMENDS", pkg, True, d)
> +    runtime_mapping_rename("RSUGGESTS", pkg, True, d)
> +    runtime_mapping_rename("RPROVIDES", pkg, True, d)
> +    runtime_mapping_rename("RREPLACES", pkg, True, d)
> +    runtime_mapping_rename("RCONFLICTS", pkg, True, d)
>  
> diff --git a/meta/classes/populate_sdk_base.bbclass b/meta/classes/populate_sdk_base.bbclass
> index 235d672..3a7927a 100644
> --- a/meta/classes/populate_sdk_base.bbclass
> +++ b/meta/classes/populate_sdk_base.bbclass
> @@ -54,7 +54,7 @@ fakeroot python do_populate_sdk() {
>      from oe.manifest import create_manifest, Manifest
>  
>      pn = d.getVar('PN', True)
> -    runtime_mapping_rename("TOOLCHAIN_TARGET_TASK", pn, d)
> +    runtime_mapping_rename("TOOLCHAIN_TARGET_TASK", pn, False, d)
>  
>      # create target/host SDK manifests
>      create_manifest(d, manifest_dir=d.getVar('SDK_DIR', True),
Ming Liu - March 7, 2014, 7:51 a.m.
On 03/07/2014 03:26 PM, Richard Purdie wrote:
> On Wed, 2014-03-05 at 18:08 +0800, Ming Liu wrote:
>> For multilib builds that rpm is the first package backend, it would be
>> often desireable to express that a package of compatible architecture
>> is needed to satisfy a dependency. In most of the cases this is already
>> handled by the automatically extracted soname dependencies, but this is
>> not always the case: sometimes it's necessary to disable the automatic
>> dependency generation, and then there are cases where the information
>> cannot be automatically generated, such as: -dev package dependencies
>> on other -dev packages or plugins are dynamically loaded via dlopen,
>> leading to obscure build failure that a 32bit package would incorrectly
>> satisfy the dependency for a 64bit package and similarly vice versa.
>>
>> The patch mainly aims to resolve this problem, the basic idea is adding
>> a ARCH_SPECIFIC variable for packages satisfying following scenarios:
>>    * when rpm is being set as the first package backend
> I don't like this idea at all. It shouldn't matter whether rpm is set
> first or last, it should behave the same.
>
> I'm also rather dubious about this patch in general. It seems wrong to
> have to put this into generic code for what amounts to an rpm backend
> issue.
I was so thoughtless. Yes, you are correct, I should move them into 
pakcage_rpm.bbclass.

>
> Finally, this patch causes build failures in do_package_write_deb:
>
> eg:
>
> http://autobuilder.yoctoproject.org/main/builders/build-appliance/builds/61/steps/BuildImages_1/logs/stdio
> http://autobuilder.yoctoproject.org/main/builders/nightly-arm/builds/82/steps/BuildImages/logs/stdio
>
> and many more. I'm trying to get the tree stablised and having build
> failures like this is extremely frustrating as once again I'm blocked on
> merging patches until I remove this patch and run another build :(
Sorry about that, I only test it with rpm and ipk, then I thought it 
should not impact deb, I will try to figure out the V2 patch and give it 
a full test.

//Ming Liu
>
> Cheers,
>
> Richard
>
>
>
>>    * it's not a allarch package
>>    * it only provides libs without bins meanwhile
>>
>> Every satisfied package would be set with
>> "ARCH_SPECIFIC_pkgname = 1:${TARGET_ARCH}" and it's gonna be written into
>> pkgdata if it exists, to mark the package as architecture specific, and
>> it will be checked during runtime_mapping_rename, all architecture
>> specific dependencies would be expanded with a ${TARGET_ARCH} suffix,
>> that declares a dependency on a package name architecture specific and
>> permits differentiating between 32-bit and 64-bit versions.
>>
>> Off course, the user could set "ARCH_SPECIFIC_pkgname = 0" in recipes to
>> disable this feature.
>>
>> Signed-off-by: Ming Liu <ming.liu@windriver.com>
>> ---
>>   meta/classes/image.bbclass             |    6 +-
>>   meta/classes/package.bbclass           |   78 ++++++++++++++++++++++++++------
>>   meta/classes/populate_sdk_base.bbclass |    2 +-
>>   3 files changed, 68 insertions(+), 18 deletions(-)
>>
>> diff --git a/meta/classes/image.bbclass b/meta/classes/image.bbclass
>> index 28b68f9..670f173 100644
>> --- a/meta/classes/image.bbclass
>> +++ b/meta/classes/image.bbclass
>> @@ -225,9 +225,9 @@ do_rootfs[prefuncs] += "rootfs_process_ignore"
>>   # may have occurred.
>>   python rootfs_runtime_mapping() {
>>       pn = d.getVar('PN', True)
>> -    runtime_mapping_rename("PACKAGE_INSTALL", pn, d)
>> -    runtime_mapping_rename("PACKAGE_INSTALL_ATTEMPTONLY", pn, d)
>> -    runtime_mapping_rename("BAD_RECOMMENDATIONS", pn, d)
>> +    runtime_mapping_rename("PACKAGE_INSTALL", pn, False, d)
>> +    runtime_mapping_rename("PACKAGE_INSTALL_ATTEMPTONLY", pn, False, d)
>> +    runtime_mapping_rename("BAD_RECOMMENDATIONS", pn, False, d)
>>   }
>>   do_rootfs[prefuncs] += "rootfs_runtime_mapping"
>>   
>> diff --git a/meta/classes/package.bbclass b/meta/classes/package.bbclass
>> index 0018a62..3e0ca30 100644
>> --- a/meta/classes/package.bbclass
>> +++ b/meta/classes/package.bbclass
>> @@ -346,19 +346,29 @@ def copydebugsources(debugsrcdir, d):
>>   # Package data handling routines
>>   #
>>   
>> -def get_package_mapping (pkg, basepkg, d):
>> +def get_package_mapping (pkg, basepkg, check_arch, d):
>>       import oe.packagedata
>>   
>> +    ret = pkg
>>       data = oe.packagedata.read_subpkgdata(pkg, d)
>> -    key = "PKG_%s" % pkg
>> +    pkg_key = "PKG_%s" % pkg
>> +    arch_key = "ARCH_SPECIFIC_%s" % pkg
>> +    arch_specific = '0'
>>   
>> -    if key in data:
>> +    if pkg_key in data:
>>           # Have to avoid undoing the write_extra_pkgs(global_variants...)
>> -        if bb.data.inherits_class('allarch', d) and data[key] == basepkg:
>> +        if bb.data.inherits_class('allarch', d) and data[pkg_key] == basepkg:
>>               return pkg
>> -        return data[key]
>> +        ret = data[pkg_key]
>>   
>> -    return pkg
>> +    if arch_key in data:
>> +        arch_specific = data[arch_key]
>> +
>> +    items = arch_specific.split(':')
>> +    if len(items) > 1 and items[0] == '1' and check_arch == True:
>> +        ret += '(' + items[1] + ')'
>> +
>> +    return ret
>>   
>>   def get_package_additional_metadata (pkg_type, d):
>>       base_key = "PACKAGE_ADD_METADATA"
>> @@ -371,13 +381,13 @@ def get_package_additional_metadata (pkg_type, d):
>>           metadata_fields = [field.strip() for field in oe.data.typed_value(key, d)]
>>           return "\n".join(metadata_fields).strip()
>>   
>> -def runtime_mapping_rename (varname, pkg, d):
>> +def runtime_mapping_rename (varname, pkg, check_arch, d):
>>       #bb.note("%s before: %s" % (varname, d.getVar(varname, True)))
>>   
>>       new_depends = {}
>>       deps = bb.utils.explode_dep_versions2(d.getVar(varname, True) or "")
>>       for depend in deps:
>> -        new_depend = get_package_mapping(depend, pkg, d)
>> +        new_depend = get_package_mapping(depend, pkg, check_arch, d)
>>           new_depends[new_depend] = deps[depend]
>>   
>>       d.setVar(varname, bb.utils.join_deps(new_depends, commasep=False))
>> @@ -1119,6 +1129,34 @@ python package_fixsymlinks () {
>>           d.setVar('RDEPENDS_' + pkg, bb.utils.join_deps(rdepends, commasep=False))
>>   }
>>   
>> +python package_check_arch () {
>> +    import glob, re
>> +
>> +    def check_arch_specific(pkg):
>> +        bin_re = re.compile(".*/s?" + os.path.basename(d.getVar("bindir", True)) + "$")
>> +        lib_re = re.compile(".*/" + os.path.basename(d.getVar("libdir", True)) + "($|/.*$)")
>> +        has_bins = 0
>> +        has_libs = 0
>> +
>> +        for file in pkgfiles[pkg]:
>> +            root = os.path.dirname(file)
>> +            if bin_re.match(root):
>> +                has_bins = 1
>> +            if lib_re.match(root):
>> +                has_libs = 1
>> +
>> +        if not bb.data.inherits_class('allarch', d) and not has_bins and has_libs:
>> +            if not d.getVar('ARCH_SPECIFIC_' + pkg):
>> +                d.setVar('ARCH_SPECIFIC_' + pkg, "%s:%s" % ('1', d.getVar('TARGET_ARCH', True)))
>> +
>> +    # Do nothing if rpm is not the first package backend
>> +    if d.getVar('PACKAGE_CLASSES', True).split()[0] != "package_rpm":
>> +        return
>> +
>> +    for pkg in sorted((d.getVar('PACKAGES', True) or "").split()):
>> +        check_arch_specific(pkg)
>> +}
>> +
>>   PKGDESTWORK = "${WORKDIR}/pkgdata"
>>   
>>   python emit_pkgdata() {
>> @@ -1184,6 +1222,16 @@ python emit_pkgdata() {
>>               pkgval = pkg
>>               d.setVar('PKG_%s' % pkg, pkg)
>>   
>> +        arch_specific = d.getVar('ARCH_SPECIFIC_%s' % pkg, True) or ""
>> +        items = arch_specific.split(':')
>> +        if len(items) > 1 and items[0] == '1':
>> +            rprovides = d.getVar('RPROVIDES_%s' % pkg, True)
>> +            if rprovides == None:
>> +                rprovides = pkg + ' (=' + d.getVar('EXTENDPKGV', True) + ')'
>> +            else:
>> +                rprovides += ' ' + pkg + ' (=' + d.getVar('EXTENDPKGV', True) + ')'
>> +            d.setVar('RPROVIDES_%s' % pkg, rprovides)
>> +
>>           pkgdestpkg = os.path.join(pkgdest, pkg)
>>           files = {}
>>           total_size = 0
>> @@ -1215,6 +1263,7 @@ python emit_pkgdata() {
>>           write_if_exists(sf, pkg, 'SECTION')
>>           write_if_exists(sf, pkg, 'PKG')
>>           write_if_exists(sf, pkg, 'ALLOW_EMPTY')
>> +        write_if_exists(sf, pkg, 'ARCH_SPECIFIC')
>>           write_if_exists(sf, pkg, 'FILES')
>>           write_if_exists(sf, pkg, 'pkg_postinst')
>>           write_if_exists(sf, pkg, 'pkg_postrm')
>> @@ -1878,6 +1927,7 @@ PACKAGESPLITFUNCS ?= " \
>>   PACKAGEFUNCS += " \
>>                   package_fixsymlinks \
>>                   package_name_hook \
>> +                package_check_arch \
>>                   package_do_filedeps \
>>                   package_do_shlibs \
>>                   package_do_pkgconfig \
>> @@ -2010,10 +2060,10 @@ def mapping_rename_hook(d):
>>       like debian.bbclass or manual PKG variable name changes
>>       """
>>       pkg = d.getVar("PKG", True)
>> -    runtime_mapping_rename("RDEPENDS", pkg, d)
>> -    runtime_mapping_rename("RRECOMMENDS", pkg, d)
>> -    runtime_mapping_rename("RSUGGESTS", pkg, d)
>> -    runtime_mapping_rename("RPROVIDES", pkg, d)
>> -    runtime_mapping_rename("RREPLACES", pkg, d)
>> -    runtime_mapping_rename("RCONFLICTS", pkg, d)
>> +    runtime_mapping_rename("RDEPENDS", pkg, True, d)
>> +    runtime_mapping_rename("RRECOMMENDS", pkg, True, d)
>> +    runtime_mapping_rename("RSUGGESTS", pkg, True, d)
>> +    runtime_mapping_rename("RPROVIDES", pkg, True, d)
>> +    runtime_mapping_rename("RREPLACES", pkg, True, d)
>> +    runtime_mapping_rename("RCONFLICTS", pkg, True, d)
>>   
>> diff --git a/meta/classes/populate_sdk_base.bbclass b/meta/classes/populate_sdk_base.bbclass
>> index 235d672..3a7927a 100644
>> --- a/meta/classes/populate_sdk_base.bbclass
>> +++ b/meta/classes/populate_sdk_base.bbclass
>> @@ -54,7 +54,7 @@ fakeroot python do_populate_sdk() {
>>       from oe.manifest import create_manifest, Manifest
>>   
>>       pn = d.getVar('PN', True)
>> -    runtime_mapping_rename("TOOLCHAIN_TARGET_TASK", pn, d)
>> +    runtime_mapping_rename("TOOLCHAIN_TARGET_TASK", pn, False, d)
>>   
>>       # create target/host SDK manifests
>>       create_manifest(d, manifest_dir=d.getVar('SDK_DIR', True),
>
>
>

Patch

diff --git a/meta/classes/image.bbclass b/meta/classes/image.bbclass
index 28b68f9..670f173 100644
--- a/meta/classes/image.bbclass
+++ b/meta/classes/image.bbclass
@@ -225,9 +225,9 @@  do_rootfs[prefuncs] += "rootfs_process_ignore"
 # may have occurred.
 python rootfs_runtime_mapping() {
     pn = d.getVar('PN', True)
-    runtime_mapping_rename("PACKAGE_INSTALL", pn, d)
-    runtime_mapping_rename("PACKAGE_INSTALL_ATTEMPTONLY", pn, d)
-    runtime_mapping_rename("BAD_RECOMMENDATIONS", pn, d)
+    runtime_mapping_rename("PACKAGE_INSTALL", pn, False, d)
+    runtime_mapping_rename("PACKAGE_INSTALL_ATTEMPTONLY", pn, False, d)
+    runtime_mapping_rename("BAD_RECOMMENDATIONS", pn, False, d)
 }
 do_rootfs[prefuncs] += "rootfs_runtime_mapping"
 
diff --git a/meta/classes/package.bbclass b/meta/classes/package.bbclass
index 0018a62..3e0ca30 100644
--- a/meta/classes/package.bbclass
+++ b/meta/classes/package.bbclass
@@ -346,19 +346,29 @@  def copydebugsources(debugsrcdir, d):
 # Package data handling routines
 #
 
-def get_package_mapping (pkg, basepkg, d):
+def get_package_mapping (pkg, basepkg, check_arch, d):
     import oe.packagedata
 
+    ret = pkg
     data = oe.packagedata.read_subpkgdata(pkg, d)
-    key = "PKG_%s" % pkg
+    pkg_key = "PKG_%s" % pkg
+    arch_key = "ARCH_SPECIFIC_%s" % pkg
+    arch_specific = '0'
 
-    if key in data:
+    if pkg_key in data:
         # Have to avoid undoing the write_extra_pkgs(global_variants...)
-        if bb.data.inherits_class('allarch', d) and data[key] == basepkg:
+        if bb.data.inherits_class('allarch', d) and data[pkg_key] == basepkg:
             return pkg
-        return data[key]
+        ret = data[pkg_key]
 
-    return pkg
+    if arch_key in data:
+        arch_specific = data[arch_key]
+
+    items = arch_specific.split(':')
+    if len(items) > 1 and items[0] == '1' and check_arch == True:
+        ret += '(' + items[1] + ')'
+
+    return ret
 
 def get_package_additional_metadata (pkg_type, d):
     base_key = "PACKAGE_ADD_METADATA"
@@ -371,13 +381,13 @@  def get_package_additional_metadata (pkg_type, d):
         metadata_fields = [field.strip() for field in oe.data.typed_value(key, d)]
         return "\n".join(metadata_fields).strip()
 
-def runtime_mapping_rename (varname, pkg, d):
+def runtime_mapping_rename (varname, pkg, check_arch, d):
     #bb.note("%s before: %s" % (varname, d.getVar(varname, True)))
 
     new_depends = {}
     deps = bb.utils.explode_dep_versions2(d.getVar(varname, True) or "")
     for depend in deps:
-        new_depend = get_package_mapping(depend, pkg, d)
+        new_depend = get_package_mapping(depend, pkg, check_arch, d)
         new_depends[new_depend] = deps[depend]
 
     d.setVar(varname, bb.utils.join_deps(new_depends, commasep=False))
@@ -1119,6 +1129,34 @@  python package_fixsymlinks () {
         d.setVar('RDEPENDS_' + pkg, bb.utils.join_deps(rdepends, commasep=False))
 }
 
+python package_check_arch () {
+    import glob, re
+
+    def check_arch_specific(pkg):
+        bin_re = re.compile(".*/s?" + os.path.basename(d.getVar("bindir", True)) + "$")
+        lib_re = re.compile(".*/" + os.path.basename(d.getVar("libdir", True)) + "($|/.*$)")
+        has_bins = 0
+        has_libs = 0
+
+        for file in pkgfiles[pkg]:
+            root = os.path.dirname(file)
+            if bin_re.match(root):
+                has_bins = 1
+            if lib_re.match(root):
+                has_libs = 1
+
+        if not bb.data.inherits_class('allarch', d) and not has_bins and has_libs:
+            if not d.getVar('ARCH_SPECIFIC_' + pkg):
+                d.setVar('ARCH_SPECIFIC_' + pkg, "%s:%s" % ('1', d.getVar('TARGET_ARCH', True)))
+
+    # Do nothing if rpm is not the first package backend
+    if d.getVar('PACKAGE_CLASSES', True).split()[0] != "package_rpm":
+        return
+
+    for pkg in sorted((d.getVar('PACKAGES', True) or "").split()):
+        check_arch_specific(pkg)
+}
+
 PKGDESTWORK = "${WORKDIR}/pkgdata"
 
 python emit_pkgdata() {
@@ -1184,6 +1222,16 @@  python emit_pkgdata() {
             pkgval = pkg
             d.setVar('PKG_%s' % pkg, pkg)
 
+        arch_specific = d.getVar('ARCH_SPECIFIC_%s' % pkg, True) or ""
+        items = arch_specific.split(':')
+        if len(items) > 1 and items[0] == '1':
+            rprovides = d.getVar('RPROVIDES_%s' % pkg, True)
+            if rprovides == None:
+                rprovides = pkg + ' (=' + d.getVar('EXTENDPKGV', True) + ')'
+            else:
+                rprovides += ' ' + pkg + ' (=' + d.getVar('EXTENDPKGV', True) + ')'
+            d.setVar('RPROVIDES_%s' % pkg, rprovides)
+
         pkgdestpkg = os.path.join(pkgdest, pkg)
         files = {}
         total_size = 0
@@ -1215,6 +1263,7 @@  python emit_pkgdata() {
         write_if_exists(sf, pkg, 'SECTION')
         write_if_exists(sf, pkg, 'PKG')
         write_if_exists(sf, pkg, 'ALLOW_EMPTY')
+        write_if_exists(sf, pkg, 'ARCH_SPECIFIC')
         write_if_exists(sf, pkg, 'FILES')
         write_if_exists(sf, pkg, 'pkg_postinst')
         write_if_exists(sf, pkg, 'pkg_postrm')
@@ -1878,6 +1927,7 @@  PACKAGESPLITFUNCS ?= " \
 PACKAGEFUNCS += " \
                 package_fixsymlinks \
                 package_name_hook \
+                package_check_arch \
                 package_do_filedeps \
                 package_do_shlibs \
                 package_do_pkgconfig \
@@ -2010,10 +2060,10 @@  def mapping_rename_hook(d):
     like debian.bbclass or manual PKG variable name changes
     """
     pkg = d.getVar("PKG", True)
-    runtime_mapping_rename("RDEPENDS", pkg, d)
-    runtime_mapping_rename("RRECOMMENDS", pkg, d)
-    runtime_mapping_rename("RSUGGESTS", pkg, d)
-    runtime_mapping_rename("RPROVIDES", pkg, d)
-    runtime_mapping_rename("RREPLACES", pkg, d)
-    runtime_mapping_rename("RCONFLICTS", pkg, d)
+    runtime_mapping_rename("RDEPENDS", pkg, True, d)
+    runtime_mapping_rename("RRECOMMENDS", pkg, True, d)
+    runtime_mapping_rename("RSUGGESTS", pkg, True, d)
+    runtime_mapping_rename("RPROVIDES", pkg, True, d)
+    runtime_mapping_rename("RREPLACES", pkg, True, d)
+    runtime_mapping_rename("RCONFLICTS", pkg, True, d)
 
diff --git a/meta/classes/populate_sdk_base.bbclass b/meta/classes/populate_sdk_base.bbclass
index 235d672..3a7927a 100644
--- a/meta/classes/populate_sdk_base.bbclass
+++ b/meta/classes/populate_sdk_base.bbclass
@@ -54,7 +54,7 @@  fakeroot python do_populate_sdk() {
     from oe.manifest import create_manifest, Manifest
 
     pn = d.getVar('PN', True)
-    runtime_mapping_rename("TOOLCHAIN_TARGET_TASK", pn, d)
+    runtime_mapping_rename("TOOLCHAIN_TARGET_TASK", pn, False, d)
 
     # create target/host SDK manifests
     create_manifest(d, manifest_dir=d.getVar('SDK_DIR', True),