name conflicts of .so file in android binary - bazel

I'm linking a cc_library to an android_binary and getting a naming issue. Can someone tells me how to solve it?
The cc_library:
cc_library(
name = "native_library",
srcs = glob(["libs/**/*.so"])
)
The contents of libs directory:
libs
├── armeabi
│   ├── libSound.so
│   ├── libSec.so
│   ├── libWatch.so
│   └── libTec.so
├── armeabi-v7a
│   ├── libSound.so
│   ├── libSec.so
│   └── libWatch.so
├── x86
│   ├── libSound.so
│   ├── libSec.so
│   ├── libWatch.so
│   └── libTec.so
|—— other jars
And The error messages are like this:
ERROR: /the/path/to/BUILD:10:1: in android_binary rule //:debug_apk: Each library in the transitive closure must have a unique basename to avoid name collisions when packaged into an apk, but two libraries have the basename 'libSound.so': libs/armeabi/libSound.so and libs/armeabi-v7a/libSound.so.
...

An alternate approach that takes advantage of android_binary's --fat_apk_cpu flag and does not require renaming your libraries:
android_binary will build each cc_library once for each architecture specified by --fat_apk_cpu. The default of --fat_apk_cpu is just armeabi-v7a. This is known as the "Android split transition". When it builds each cc_library, that cc_library gets passed a --cpu flag from the list in --fat_apk_cpu. We can define config_setting rules that read these flags, and use a select statement in the cc_library, so that your cc_library contains different .so files depending on which architecture that it is built for.
For example:
# BUILD
CPUS = ["armeabi", "armeabi-v7a", "x86"]
[config_setting(name = cpu, values = {"cpu": cpu}) for cpu in CPUS]
cc_library(
name = "native_library",
srcs = select(
{":%s" % cpu : glob(["libs/%s/*.so" % cpu]) for cpu in CPUS}
),
)
android_binary(
name = "app",
srcs = glob(["*.java"]),
manifest = "AndroidManifest.xml",
deps = [":native_library"],
)
And then on the command line you can specify which architectures you want present in the final APK.
$ bazel build --fat_apk_cpu=armeabi,armeabi-v7a,x86 //:app
$ zipinfo -1 bazel-bin/app.apk | grep \.so$
lib/x86/libWatch.so
lib/x86/libSound.so
lib/x86/libSec.so
lib/x86/libTec.so
lib/armeabi-v7a/libWatch.so
lib/armeabi-v7a/libSound.so
lib/armeabi-v7a/libSec.so
lib/armeabi-v7a/libTec.so
lib/armeabi/libWatch.so
lib/armeabi/libSound.so
lib/armeabi/libSec.so
lib/armeabi/libTec.so
$ bazel build --fat_apk_cpu=x86 //:app
$ zipinfo -1 bazel-bin/app.apk | grep \.so$
lib/x86/libWatch.so
lib/x86/libSound.so
lib/x86/libSec.so
lib/x86/libTec.so
Specifying only one architecture to build for can speed up your development builds. For example, if you use an x86 emulator while you develop, you don't need the armeabi and armeabi-v7a .so files.

I might be wrong but it's the limitation of apk layout, so I'm afraid you just cannot have that named libs in a fat apk. Is renaming libs into libSound-armeabi.so etc an option for you?

Related

How to resolve a dependency in an external package workspace_file?

Trying to use a target in the build_file from an external package imported through http_archive that has dependencies defined in the external package workspace via the workspace_file attribute fails. I'm using Bazel 0.27.0 on Debian Testing.
The documentation only talks about referencing targets in the provided build_file, but I could not find any information how one could reference a dependency defined in the provided workspace_file in the provided build_file.
The usual #stringtemplate3//jar syntax fails, but I don't know how I could include a reference to the imported archive which according to the manual would have to start with #antlr3_runtimes.
The project layout looks like this:
├── antlr.BUILD
├── antlr.WORKSPACE
├── BUILD
├── external_dependency
│ └── src
│ └── main
│ └── java
│ └── bazel
│ ├── BUILD
│ └── Hello.java
├── LICENSE
└── WORKSPACE
The WORKSPACE definition looks like this:
workspace(name="bazel")
load("#bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "antlr3_runtimes",
sha256 = "d4f7d3c38c5523f8009ff37528e5797c81adb454be6acc9af507cfcb41f2016f",
strip_prefix = "antlr3-master",
urls = ["https://github.com/ibre5041/antlr3/archive/master.tar.gz"],
build_file = "#//:antlr.BUILD",
workspace_file = "#//:antlr.WORKSPACE",
)
It seems that the provided workspace_file is not even analyzed. The build already fails when it tries to resolve the dependencies in the custom build file.
A repro can be found here: https://github.com/marcohu/bazel
bazel build //... shows this error message:
ERROR: /home/user/.cache/bazel/_bazel_user/64492308e78c9898c41f12c18dd29b63/external/antlr3_runtimes/BUILD.bazel:43:1: no such package '#stringtemplate3//jar': The repository '#stringtemplate3' could not be resolved and referenced by '#antlr3_runtimes//:antlr3_tool'
ERROR: Analysis of target '//external_dependency/src/main/java/bazel:hello' failed; build aborted: no such package '#stringtemplate3//jar': The repository '#stringtemplate3' could not be resolved
I reported this in the Bazel issue tracker, but it got rejected with a hint to post here.
Is this use case something that is just not possible? Or did I got the syntax wrong?
At least as of now (I suppose this statement may change in the future versions), bazel does not directly support transitive external dependencies. WORKSPACE file would still get read in even in your case and if it contained entirely broken syntax it'd still fail, but it does not get "acted upon" and you could (currently) for instance load from non-existing labels or call undefined functions and would still be left none the wiser for a nested WORKSPACE.
You essentially have two options:
Repeat your nested dependencies (http_archive rules) in your "parent"/top WORKSPACE.
You can define a function(s) with corresponding repository rules that you load and call in your "parent"/top WORKSPACE.
Bazel actually does support the transitive fetching of jvm dependencies. https://github.com/bazelbuild/rules_jvm_external
WORKSPACE
load("#bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
rules_jvm_external_tag = "2.0.1"
rules_jvm_external_sha = "55e8d3951647ae3dffde22b4f7f8dee11b3f70f3f89424713debd7076197eaca"
http_archive(
name = "rules_jvm_external",
sha256 = rules_jvm_external_sha,
strip_prefix = "rules_jvm_external-%s" % rules_jvm_external_tag,
url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % rules_jvm_external_tag,
)
load("#rules_jvm_external//:defs.bzl", "maven_install")
maven_install(
name = "maven",
artifacts = [
"io.grpc:grpc-netty-shaded:1.22.1",
"io.grpc:grpc-api:1.22.1",
"io.grpc:grpc-testing:1.22.1",
"io.grpc:grpc-core:1.22.1",
"junit:junit:4.12",
],
repositories = [
"https://jcenter.bintray.com/",
"https://repo1.maven.org/maven2",
],
)
BUILD
...
java_library(
name = "hyphenation-service",
srcs = ["src/test/java/com/example/hyphenation/HyphenationServiceTest.java"],
deps = ["#maven//:io_grpc_grpc_core"]
)
...
Example repository https://github.com/mancini0/bazel-grpc-playground

What does the "#" symbol mean in Bazel?

I'm studying Bazel building system at present. I always see the # symbol in Bazel script, but I cannot find any documentation about it. I searched it on the website of Bazel but the result seems useless.
# in Bazel.
For example:
filegroup(
name = "toolchain_fg",
srcs = [
":cc-compiler-amd64",
"#x86_64_unknown_linux_gnu_gcc_730//:compiler_components",
],
)
Could anybody explain the # symbol here for me?
This is to reference a remote repository.
From the doc, depending on other Bazel projects
local_repository(
name = "coworkers_project",
path = "/path/to/coworkers-project",
)
If your coworker has a target //foo:bar, your project can refer to it
as #coworkers_project//foo:bar.
See also the design doc of remote repository and bind example in workspace rules.
In Bazel, targets are referred by labels.
Bazel labels have the following format:
#repoName//packageName:target
For example, in the following packages found in myRepo:
myRepo
├── WORKSPACE
├── package1
│ └── BUILD
│ └── src
└── package2
├── BUILD
└── src
a target called myTarget in package1/BUILD can be labeled as as #myRepo//package1:myTarget globally.
If referenced from the same repo, for example from package2/BUILD, then the #myRepo prefix can be omitted and you can use //package1:myTarget.
If referenced from the same package, for example in another target from package1/BUILD, then the package name can be omitted and you can use :myTarget. The colon can also be omitted if it does not create confusion with a name. Such short form labels should not be confused with the names. Labels start with '//' or ':'. But names never do. For example, the name of the first package is package1 but its label is //package1.
Reference: https://docs.bazel.build/versions/master/build-ref.html

Building a simple library with bazel, fixing include path

I have a very simple directory structure:
.
├── libs
│   └── foo
│   ├── BUILD
│   ├── include
│   │   └── foo
│   │   └── func.h
│   └── src
│   └── func.cxx
└── WORKSPACE
With func.h:
#pragma once
int square(int );
And func.cxx:
#include <foo/func.h>
int square(int i) { return i * i; }
And BUILD:
cc_library(
name = "foo",
srcs = ["src/func.cxx"],
hdrs = ["include/foo/func.h"],
visibility = ["//visibility:public"],
)
This fails to build:
$ bazel build //libs/foo
INFO: Analysed target //libs/foo:foo (0 packages loaded).
INFO: Found 1 target...
ERROR: /home/brevzin/sandbox/bazel/libs/foo/BUILD:1:1: C++ compilation of rule '//libs/foo:foo' failed (Exit 1)
libs/foo/src/func.cxx:1:22: fatal error: foo/func.h: No such file or directory
#include <foo/func.h>
^
compilation terminated.
Target //libs/foo:foo failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 0.299s, Critical Path: 0.02s
FAILED: Build did NOT complete successfully
How do I set the include path properly? I tried using include_prefix (whether include or include/foo) but that didn't change the behavior.
Hmm, the tricky part about including headers from another places is that you have to specify the header file from its relative location according to the workspace (where the WORKSPACE file resides).
Moreover, you should not use the angle-bracket including style #include <a/b.h> unless you are including the system header files.
The related specifications for #include can be found here: https://docs.bazel.build/versions/master/bazel-and-cpp.html#include-paths
TL;DR The only change you need to make to your func.cxx file is that, change the first line to #include "libs/foo/include/foo/func.h".
And then, when you run bazel build //... (build all targets in this workspace, similar to make makes all) from the root of the workspace, you will encounter no error.
However, this is not the only way that can solve your problem.
Another way to resolve this issue that does not involve changing the source code include statement would be specifying the include path in the attribute of the rule cc_library.
Which means, you can make changes to your BUILD in path libs/foo to make it look like this:
cc_library(
name = "foo",
srcs = ["src/func.cxx"],
hdrs = ["include/foo/func.h"],
copts = ["-Ilibs/foo/include"], # This is the key part
visibility = ["//visibility:public"],
)
After this change, the compiler will be able to figure out where to find the header file(s), and you don't have to change your source code, yay.
Related information can be found here: https://docs.bazel.build/versions/master/cpp-use-cases.html#adding-include-paths
Nevertheless, there is another hacky way to solve your problem, however, it involves in more changes to your code.
It makes uses of the rule cc_inc_library.
The rule cc_inc_library will strips prefix attribute passed to this rule from the relative path of the header files specified in the hdrs attribute.
The example on the website is a little confusing, your code and directory structure will yield a much better demonstration purpose.
In your case, you have to modify your BUILD file under libs/foo to something that looks like this:
cc_library(
name = "foo",
srcs = ["src/func.cxx"],
deps = [":lib"],
copts = ["-Ilibs/foo/include"],
visibility = ["//visibility:public"],
)
cc_inc_library(
name = "lib",
hdrs = ["include/foo/func.h"],
prefix = "include/foo",
)
In your case, the header file func.h which has a relative path from the package libs/foo as include/foo/func.h, which is specified in the hdrs attribute.
Since it has a relative path to the root of the workspace as libs/foo/include/foo/func.h, and the prefix attribute in the cc_inc_library is specified as include/foo: the value include/foo will be stripped form lib/foo/include/foo/func.h, making it libs/foo/func.h.
So, now you can include this header file in your func.cxx as #include "libs/foo/func.h".
And now, bazel will not report error saying that it was not able to find the header file.
You can find the information about this rule at: https://docs.bazel.build/versions/master/be/c-cpp.html#cc_inc_library.
However, as stated above, the explanation is confusing at best, possibly because the documentation for it is out of date.
I was puzzled by the office explanation on bazel.build for quite some time until I read the source code for this rule at: https://github.com/bazelbuild/bazel/blob/f20ae6b20816df6a393c6e8352befba9b158fdf4/src/main/java/com/google/devtools/build/lib/rules/cpp/CcIncLibrary.java#L36-L50
The comment for the actual code that implements the function does a much, much better job at explaining what this rule actually does.
cc_inc_library rule has been deprecated since Bazel release 0.12.
Use cc_library approach instead.
See: https://blog.bazel.build/2018/04/11/bazel-0.12.html
What you really want here is strip_include_prefix:
cc_library(
name = "foo",
srcs = ["src/func.cxx"],
hdrs = ["include/foo/func.h"],
# Here!
strip_include_prefix = "include",
visibility = ["//visibility:public"],
)
This will make the headers accessible via:
#include "foo/func.h"
This attribute has been available since at least Bazel 0.17.

Pod lib lint did not pass validation xcodebuild: ld: warning: ignoring file missing required architecture i386 in file

Recently, I'm trying to use CocoaPods to upload an integrated sharedSDK to a private library.
I'm not sure if I'm doing something stupid. When I run "pod lib lint", everything seems to be fine but when I run "pod spec lint" I get:
ld: warning: ignoring file by TencentOpenAPI.framework
** BUILD FAILED **
The following build commands failed:
Ld /Users/melody/Library/Developer/Xcode/DerivedData/App-fcbstzvalzvjwfgtkwkycnyvprjk
/Build/Intermediates/App.build/Release-iphonesimulator/App.build/Objects-normal/i386/App normal i386
(1 failure)
-> SLShareSDK (0.1.0)
- ERROR | [iOS] xcodebuild: Returned an unsuccessful exit code.
- NOTE | [iOS] xcodebuild: ld: warning: ignoring file /Users/melody/GItLab/SLShareSDK/SLShareSDK/Assets/TencentOpenAPI.framework/TencentOpenAPI,
missing required architecture i386 in file /Users/melody/GItLab/SLShareSDK/SLShareSDK/Assets/TencentOpenAPI.framework/TencentOpenAPI (3 slices)
- NOTE | [iOS] xcodebuild: clang: error: linker command failed with exit code 1 (use -v to see invocation)
[!] SLShareSDK did not pass validation, due to 1 error.
The contents of my SLShareSDK.Sperepo file are
Pod::Spec.new do |s|
s.name = 'SLShareSDK'
s.version = '0.1.0'
s.summary = 'Intergation'
# This description is used to generate tags and improve search results.
# * Think: What does it do? Why did you write it? What is the focus?
# * Try to keep it short, snappy and to the point.
# * Write the description between the DESC delimiters below.
# * Finally, don't worry about the indent, CocoaPods strips it!
s.description = <<-DESC
TODO: SLShare...
DESC
s.homepage = 'http://gitlaBalaBala...'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'Melody' => 'BalaBala.com' }
s.source = { :git => 'http://gitlab.balabala...../SLShareSDK.git', :tag => s.version.to_s }
s.ios.deployment_target = '7.0'
s.source_files = 'SLShareSDK/Classes/**/*'
s.ios.vendored_libraries = 'SLShareSDK/Assets/*.a'
s.ios.vendored_frameworks = 'SLShareSDK/Assets/TencentOpenAPI.framework'
s.resource_bundles = {
'SLShareSDK' => ['SLShareSDK/Assets/*.bundle']
}
s.frameworks = 'UIKit', 'SystemConfiguration','Security','CoreTelephony','ImageIO'
s.libraries = 'sqlite3','z.1.2.8','c++.1'
s.dependency 'AFNetworking', '~> 2.6.3'
end
Here is the directory structure :
├── SLShareSDK
│   ├── Assets
│   │   ├── TencentOpenAPI.framework
│   │   │   ├── Headers
│   │   │   ├── Resources
│   │   │   └── TencentOpenAPI
│   │   ├── libWeChatSDK.a
│   │   └── libWeiboSDK.a
│   └── Classes
│   └── SLShareSDK
│   ├── AFHTTPRequestOperationManager+JXExtentsion.h
│   ├── AFHTTPRequestOperationManager+JXExtentsion.m
│   ├── SLShare.h
│   ├── SLShare.m
│   ├── SLShareAuthorizeObject.h
│   ├── SLShareAuthorizeObject.m
│   ├── SLShareConfiguration.plist
│   ├── SLShareConstant.h
│   ├── SLShareConstant.m
│   ├── SLShareContentObject.h
│   ├── SLShareContentObject.m
│   ├── SLShareContentObjectTranslater.h
│   ├── SLShareContentObjectTranslater.m
│   ├── SLShareQQ.h
│   ├── SLShareQQ.m
│   ├── SLShareSina.h
│   ├── SLShareSina.m
│   ├── SLShareWeChat.h
│   ├── SLShareWeChat.m
│   ├── WBHttpRequest+WeiboGame.h
│   ├── WBHttpRequest+WeiboShare.h
│   ├── WBHttpRequest+WeiboToken.h
│   ├── WBHttpRequest+WeiboUser.h
│   ├── WBHttpRequest.h
│   ├── WBSDKBasicButton.h
│   ├── WBSDKCommentButton.h
│   ├── WBSDKRelationshipButton.h
│   ├── WXApi.h
│   ├── WXApiObject.h
│   ├── WechatAuthSDK.h
│   ├── WeiboSDK+Statistics.h
│   ├── WeiboSDK.h
│   └── WeiboUser.h
├── SLShareSDK.podspec
Q1.:
In my Opinion,
The Program Use the TencentOpenAPI.framework and I aleady set s.ios.vendored_frameworks = 'SLShareSDK/Assets/TencentOpenAPI.framework' make sure the code path currect.
And I Use pod lib lint --use-libraries --verbose Check Out the Specrepo
But it show error make me crazed.But the way,The Same code I Use xcode run without problems.
So,This is my situation, thank you for your attention and help, if you have some suggestion please help me !!!
I also got this error when i try to link it agains third party framework, i found no solution but have to take out the header files alone out to let the lib build without linking, after that it work, would like to know a better solution though, see this for more info, also contain a fix by changing the validator.rb
I think I have found out the problem, as the error mentioned in the latest TencentOpenAPI.framework does not support i836CPU, so I try to use the old API to run, it's working!!!. But I do not know how to change pod lib lint does not run the i836 compiler...

Cordova 3.1.0 : Command Line interface not working

Cordova 3.1.0 : Command Line interface not working . I installed node.js and used th following command
$ sudo npm install -g cordova
after installation am getting following output :
/usr/local/bin/cordova -> /usr/local/lib/node_modules/cordova/bin/cordova
cordova#3.1.0-0.2.0 /usr/local/lib/node_modules/cordova
├── ncallbacks#1.0.0
├── semver#1.1.0
├── colors#0.6.2
├── open#0.0.3
├── mime#1.2.11
├── q#0.9.7
├── shelljs#0.1.2
├── follow-redirects#0.0.3 (underscore#1.5.2)
├── optimist#0.6.0 (wordwrap#0.0.2, minimist#0.0.5)
├── xcode#0.5.1 (node-uuid#1.3.3, pegjs#0.6.2)
├── glob#3.2.6 (inherits#2.0.1, minimatch#0.2.12)
├── tar#0.1.18 (inherits#2.0.1, block-stream#0.0.7, fstream#0.1.24)
├── elementtree#0.1.5 (sax#0.3.5)
├── prompt#0.2.7 (revalidator#0.1.5, pkginfo#0.3.0, read#1.0.5, utile#0.1.7, winston#0.6.2)
├── express#3.0.0 (methods#0.0.1, fresh#0.1.0, range-parser#0.0.4, debug#0.7.3, cookie#0.0.4, crc#0.2.0, commander#0.6.1, mkdirp#0.3.3, send#0.1.0, connect#2.6.0)
├── request#2.22.0 (json-stringify-safe#4.0.0, aws-sign#0.3.0, forever-agent#0.5.0, qs#0.6.5, tunnel-agent#0.3.0, oauth-sign#0.3.0, cookie-jar#0.3.0, node-uuid#1.4.1, http-signature#0.10.0, hawk#0.13.1, form-data#0.0.8)
├── npm#1.3.13
├── plist#0.4.3 (xmlbuilder#0.4.2, xmldom#0.1.16)
├── ripple-emulator#0.9.18 (connect-xcors#0.5.2, colors#0.6.0-1, accounting#0.3.2, request#2.12.0, moment#1.7.2, express#3.1.0)
└── plugman#0.14.0 (ncallbacks#1.1.0, osenv#0.0.3, bplist-parser#0.0.4, semver#2.0.11, underscore#1.4.4, nopt#1.0.10, dep-graph#1.1.0, xcode#0.6.1, rc#0.3.0, tar.gz#0.1.1, npm#1.3.4)
Not able to create add platform or create a project .
while using this command
$ cordova create hello com.example.hello HelloWorld
am getting the following error.
-bash: cordova: command not found
am using MAC OS
Did your PATH system variable include /usr/local/bin/?
You need to add the NPM directory into your PATH, so that you can run "cordova" from anywhere and it will get picked up.
For example, I had to add
;C:\Users\MBillau\AppData\Roaming\npm
which is where my npm folder is.

Resources