How to build a release apk with signature in bazel - bazel

I can't find any rule about keystore in bazel's doc or any parameter about signature in android_binary's parameters. So how can I build a release apk with signature?

for the moment, Bazel does not support signing the apk.
As we can read in the docs:
android_binary creates a name_unsigned.apk: An unsigned version of the above file that could be signed with the release keys before release to the public.
This means that you should sign this unsigned apk with apksigner using other tools. Take a look at Build an unsigned APK and sign it manually from the Android documentation.

I wore a quick genrule that will do the signing for you. However, one thing to note is that you won't be able to use mobile-install on the output signed APK which is very unfortunate (still looking for a way around it).
def signed_android_binray(
name,
android_binary,
keystore,
alias,
password):
"""
This macro signs given android_binary with the given keystore
Example usage:
load("//bzl:apksigner.bzl","signed_android_binray")
signed_android_binray(
name = "signed-foo-app",
android_binary = ":foo-app",
keystore = "//path/to/keystore:my-keystore",
alias = mykeystore,
pass = foo123,
)
"""
unsigned_android_binary = android_binary + "_unsigned.apk"
output_name = "{}_signed.apk".format(name)
native.genrule(
name = name,
srcs = [
unsigned_android_binary,
keystore,
],
tools = [
"#androidsdk//:apksigner",
],
outs = [
output_name,
],
cmd = """
$(location #androidsdk//:apksigner) sign \
--ks $(location {}) \
--ks-key-alias {} \
--ks-pass pass:{} \
--v1-signing-enabled true \
--v1-signer-name CERT \
--v2-signing-enabled true \
--out $(location {}) \
$(location {})
""".format(keystore, alias, password output_name, unsigned_android_binary),
)

This question was asked on bazel-discuss mailing list, it also contains a solution plus some background.

I don't have enough stackoverflow reputation to comment on Steren's answer, but the fist part of the answer is not correct. Bazel does support signing APKs. In fact every build of an android_binary produces a signed APK, however it is signed with a hard coded debug key.
Bazel intentionally does not provide a mechanism for users to specify a key, because Bazel should not be used to sign with release keys. That would require that your release key is in your workspace, and thus would likely get checked into source control. That would be very bad practice.

Related

docker -w dir prefixed with another dir [duplicate]

Earlier today, I was trying to generate a certificate with a DNSName entry in the SubjectAltName extension:
$ openssl req -new -subj "/C=GB/CN=foo" -addext "subjectAltName = DNS:foo.co.uk" \
-addext "certificatePolicies = 1.2.3.4" -key ./private-key.pem -out ~/req.pem
This command led to the following error message:
name is expected to be in the format /type0=value0/type1=value1/type2=... where characters may be escaped by . This name is not in that format: 'C:/Program Files/Git/C=GB/CN=foo'
problems making Certificate Request
How can I stop Git Bash from treating this string parameter as a filepath, or at least stop it from making this alteration?
The release notes to the Git Bash 2.21.0 update today mentioned this as a known issue. Fortunately, they also described two solutions to the problem:
If you specify command-line options starting with a slash, POSIX-to-Windows path conversion will kick in converting e.g. "/usr/bin/bash.exe" to "C:\Program Files\Git\usr\bin\bash.exe". When that is not desired -- e.g. "--upload-pack=/opt/git/bin/git-upload-pack" or "-L/regex/" -- you need to set the environment variable MSYS_NO_PATHCONV temporarily, like so:
MSYS_NO_PATHCONV=1 git blame -L/pathconv/ msys2_path_conv.cc
Alternatively, you can double the first slash to avoid POSIX-to-Windows path conversion, e.g. "//usr/bin/bash.exe".
Using MSYS_NO_PATHCONV=1 can be problematic if your script accesses files.
Prefixing with a double forward slash doesn't work for the specific case of OpenSSL, as it causes the first DN segment key to be read as "/C" instead of "C", so OpenSSL drops it, outputting:
req: Skipping unknown attribute "/C"
Instead, I used a function that detects if running on bash for Windows, and prefixes with a "dummy" segment if so:
# If running on bash for Windows, any argument starting with a forward slash is automatically
# interpreted as a drive path. To stop that, you can prefix with 2 forward slashes instead
# of 1 - but in the specific case of openssl, that causes the first CN segment key to be read as
# "/O" instead of "O", and is skipped. We work around that by prefixing with a spurious segment,
# which will be skipped by openssl
function fixup_cn_subject() {
local result="${1}"
case $OSTYPE in
msys|win32) result="//XX=x${result}"
esac
echo "$result"
}
# Usage example
MY_SUBJECT=$(fixup_cn_subject "/C=GB/CN=foo")
Found a workaround by passing a dummy value as the first attribute, for example: -subj '//SKIP=skip/C=gb/CN=foo'
I had the same issue using bash, but running the exact same command in Powershell worked for me. Hopefully this will help someone.

bazel: How do you "request" that a cc_binary build the stripped version of the binary?

The docs for Bazel's cc_binary rule say:
Implicit output targets
<name>.stripped (only built if explicitly requested): A stripped version of the binary. strip -g is run on the binary to remove debug symbols. Additional strip options can be provided on the command line using --stripopt=-foo. This output is only built if explicitly requested.
How do I "explicitly request" that this stripped binary get built? Is there something I need to put in my cc_binary declaration in my BUILD file? I can't figure it out from the docs (or the Bazel source).
Okay I think I figured out how.
If my BUILD file has this:
cc_binary(
name = "mytool",
srcs = ["mytool.c"]
)
... then from the command line I can build the stripped binary with this:
bazel build //:mytool.stripped
or, the more common scenario, if I have another BUILD rule that needs the stripped binary as one of its inputs, I can just refer to it by that same label, :mytool.stripped. Here is sort of a weird contrived example:
genrule(
name = "mygenrule",
outs = ["genrule.out"],
srcs = [":tool1.stripped"],
# run tool1.stripped, sends its output to genrule.out:
cmd = "$(SRCS) > $#"
)

Setting environment variables in Flutter

For example, building a client for an API, like Twitch.
In a Dart CLI binary, I could use a generic environment variable, or a Dart definition variable. For example, using both as fallbacks:
main() {
String clientId =
// dart -dCLIENT_ID='abc bin/example.dart
// This is considered "compiled-into" the application.
const String.fromEnvironment('CLIENT_ID') ??
// CLIENT_ID='abc' dart bin/example.dart
// This is considered a runtime flag.
Platform.environment['CLIENT_ID'];
// Use clientId.
}
Does Flutter have a way of setting either/both of these, specifically...
During dev time
When shipped to prod
Happy to help with some docs once I figure out how :)
Starting from Flutter 1.17 you can define compile-time variables if you want to.
To do so just use --dart-define argument during flutter run or flutter build
If you need to pass multiple key-value pairs, just define --dart-define multiple times:
flutter run --dart-define=SOME_VAR=SOME_VALUE --dart-define=OTHER_VAR=OTHER_VALUE
and then, anywhere in your code you can use them like:
const SOME_VAR = String.fromEnvironment('SOME_VAR', defaultValue: 'SOME_DEFAULT_VALUE');
const OTHER_VAR = String.fromEnvironment('OTHER_VAR', defaultValue: 'OTHER_DEFAULT_VALUE');
Also, they can be used in native layers too.
Here is an article that explains more.
For configuration a common pattern I've seen is to use separate main files instead. i.e.
flutter run -t lib/production_main.dart
and
flutter build apk -t lib/debug_main.dart
And then in those different main files set up the configurations desired.
In terms of reading ids, you can do that from arbitrary assets https://flutter.io/assets-and-images/.
I believe it is possible in Flutter to read from the environment as you suggest, however I don't know how to set those environment variables on iOS or Android.
Since I was trying to solve this as well and encountered this thread I just wanted to add this for people looking for a solution in the future... If all you're looking for is PROD/DEV environments there is now a supported way of getting if the app is in production or not:
const bool isProduction = bool.fromEnvironment('dart.vm.product');
As suggested by:
https://twitter.com/FlutterDev/status/1048278525432791041
https://github.com/flutter/flutter/issues/4014
To run your app (in flutter run)
flutter run --dart-define=EXAMPLE_API_ENDPOINT=https://api.example.com/
To release your app (in flutter build)
My app wasn't letting users log in I realized that environment variables were empty strings in the app, instead of their actual values 😅.
iOS: flutter build ipa --dart-define=EXAMPLE_API_ENDPOINT=https://api.example.com/
Android: flutter build apk --dart-define=EXAMPLE_API_ENDPOINT=https://api.example.com/
--dart-define documentation
From the flutter run --help or flutter build ipa --help, the --dart-define shows:
Additional key-value pairs that will be available as
constants from the String.fromEnvironment, bool.fromEnvironment,
int.fromEnvironment, and double.fromEnvironment constructors.
Multiple defines can be passed by repeating "--dart-define"
multiple times.
I use simple shell script to generate dart defines. In my app there are 3 build flavors: dev, staging and prod. Environment variables were defined in a regular .env file.
env/
├── dev.env
├── prod.env
└── staging.env
Here is the script to generate dart-defines from .env file.
#!/bin/bash
# scripts/generate_dart_defines.sh
case "$1" in
"dev") INPUT="env/dev.env"
;;
"staging") INPUT="env/staging.env"
;;
"prod") INPUT="env/prod.env"
;;
*)
echo "Missing arguments [dev|staging|prod]"
exit 1
;;
esac
while IFS= read -r line
do
DART_DEFINES="$DART_DEFINES--dart-define=$line "
done < "$INPUT"
echo "$DART_DEFINES"
Here is the script to trigger a build.
#!/bin/bash
# build.sh
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then
echo -e "Missing arguments: [apk|appbundle|ios] [release|debug|profile] [dev|staging|prod]"
# invalid arguments
exit 128
fi
DART_DEFINES=$(scripts/generate_dart_defines.sh $3)
if [ $? -ne 0 ]; then
echo -e "Failed to generate dart defines"
exit 1
fi
echo -e "artifact: $1, type: $2, flavor: $3\n"
echo -e "DART_DEFINES: $DART_DEFINES\n"
eval "flutter build $1 --$2 --flavor $3 $DART_DEFINES"
The script accepts 3 arguments. First one is the artifact apk, appbundle or ios. Second one is the build type release, debug or profile. Third one is the build flavor, dev, staging or prod.
./build.sh apk release prod
Please note that you also required to configure android and ios for different build flavors separately.
https://developer.android.com/studio/build/build-variants
https://shockoe.com/ideas/development/how-to-setup-configurations-and-schemes-in-xcode/
https://developer.apple.com/library/archive/documentation/ToolsLanguages/Conceptual/Xcode_Overview/ManagingSchemes.html
I do agree with the answer posted by #tatsuDn but I wanted to provide a solution that loads your environment variables from a .env file.
First create a .env file in the root folder of your project.
Ensure that you add the file to your pubspec.yaml and [git] ignore it.
Here is how your .env file should look
API_KEY=sampleapikey
# This line is a comment
# The white line above will be ignored
HEADER=sampleapiheader
ANOTHER_UNIQUE_KEY=theValueOfThisKey
KEY_CONTAINS_#=*234*5#
KEY_CONTAINS_EQUALS=IP8iwe=0&
Here is how your assets section to look like.
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/
- assets/flags/
- .env
Finally, load your environment variable by reading and parsing the .env file to get a Map<String, String> that contains your key value pairs.
Future<Map<String, String>> parseStringToMap({String assetsFileName = '.env'}) async {
final lines = await rootBundle.loadString(assetsFileName);
Map<String, String> environment = {};
for (String line in lines.split('\n')) {
line = line.trim();
if (line.contains('=') //Set Key Value Pairs on lines separated by =
&&
!line.startsWith(RegExp(r'=|#'))) {
//No need to add emty keys and remove comments
List<String> contents = line.split('=');
environment[contents[0]] = contents.sublist(1).join('=');
}
}
return environment;
}
You can put a quick button in your code to test that the environment variables are being loaded properly.
ElevatedButton(
onPressed: () async {
final env = await parseStringToMap(assetsFileName: '.env');
print(env);
},
child: Text('Print Environment Variables')
),
Here is the output from the .env file above.
>>>I/flutter ( 7182): {API_KEY: sampleapikey, HEADER: sampleapiheader, ANOTHER_UNIQUE_KEY: theValueOfThisKey, KEY_CONTAINS_#: *234*5#, KEY_CONTAINS_EQUALS: IP8iwe=0&}
Notes: You will need to rerun the app (not hot reload) so that the .env assets is loaded.
You can also just load your variables in a json file[this may be helpful when you have non string environemental variables and you dont want to parse string.
To avaoid IO, it is a good Idea to just load the environment variables once and access them through out the app using service locators like GetIt.
although above answers are correct coming from python and reactjs I used dotenv and found the same for flutter to load .env file
https://pub.dev/packages/dotenv
Create a class:
import 'package:flutter/foundation.dart';
class AppUtils {
static String get clientId {
if (kDebugMode) return 'debug_id';
else if (kProfileMode) return 'profile_id';
else if (kReleaseMode) return 'production_id';
else if (kIsWeb) return 'web_mode_id';
throw ArgumentError('No mode detected');
}
}
Usage:
var id = AppUtils.clientId;

How to get static library in sdk?

Everyone who searched how to include a static library in SDK, surely read this thread from 2014. I tried what they suggested, but that didn't work.
Reading the yocto mega manual version 2.1 (yocto morty), I found in chapter 5.9.12. (Poky Reference Distribution Changes), that they added DISABLE_STATIC variable, to disable generation of static libraries. I tried adding this to my recipe, and it didn't enable adding static library to SDK:
DISABLE_STATIC = ""
I can see the library in the sysroot when building the image. But it is not getting in the SDK.
So, what exactly do I need to do to get a static library and the headers in SDK?
What worked is adding the staticdev package to ´IMAGE_INSTALL´ in local.conf, but I don't want to have to do that.
I created an example recipe, which demonstrates my problem. The directory structure is like this:
example-staticlib/
example-staticlib/example-staticlib_0.1.bb
example-staticlib/files/
example-staticlib/files/lib.c
example-staticlib/files/lib.h
example-staticlib/files/Makefile
example-staticlib_0.1.bb :
DESCRIPTION = "example stared library"
LICENSE = "LGPLv2"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/LGPL-2.0;md5=9427b8ccf5cf3df47c29110424c9641a"
SRC_URI = "file://lib.c \
file://lib.h \
file://Makefile"
PR = "r0"
S = "${WORKDIR}"
ALLOW_EMPTY_${PN} = "1"
do_install () {
oe_runmake install DEST=${D}
}
TOOLCHAIN_TARGET_TASK += "example-staticlib-dev"
TOOLCHAIN_TARGET_TASK += "example-staticlib-staticdev"
lib.c:
int foo()
{
return 42;
}
lib.h:
int foo();
Makefile:
TARGET=libexample.a
all:$(TARGET)
install :
#install -d $(DEST)/usr/lib/
#install -m 0644 $(TARGET) $(DEST)/usr/lib/
#install -d $(DEST)/usr/include/
#install -m 0644 lib.h $(DEST)/usr/include/
$(TARGET) : lib.c
$(CC) -c lib.c -o lib.o
$(AR) rcs $# lib.o
clean:
rm -rf lib.o $(TARGET)
How exactly to modify the recipe, in order to get the static library in the SDK?
Following your added example.
Adding the following line to your image recipe (or to an .bbappend, eg core-image-minimal.bbappend)
TOOLCHAIN_TARGET_TASK += "example-staticlib-staticdev"
should work for you. That will give you the .a file in the SDK, after running bitbake core-image-minimal -c populate_sdk. (Again assuming that the image used is core-image-minimal).
That your experiment to add the .a file to ${PN}-dev didn't work, is duet to the order of how files are put into packages. The order is ${PN}-dbg ${PN}-staticdev ${PN}-dev ${PN}-doc ${PN}-locale ${PACKAGE_BEFORE_PN} ${PN}. Thus, the .a file will, regardless, be put into ${PN}-staticdev, as that packages is handled prior to {PN}-dev.
Note, you add this line, TOOLCHAIN_TARGET_TASK += "example-staticlib-staticdev" to your image recipe, thus, you need to write the package name instead of PN.
I tried a way which doesn't require editing the image recipe.
example-staticlib_0.1.bb :
After do_install. I didn't use TOOLCHAIN_TARGET_TASK
FILES_${PN}-staticdev += "${libdir}/libexample.a"
RDEPENDS_${PN}-dev += "${PN}-staticdev"
BBCLASSEXTEND = "native nativesdk"

Using Xbuild with Xamarin.Android (formerly Mono for Android)

We have a Xamarin.Android project that we are trying to build using Jenkins on a Mac. The Solution file contains several different projects, one of which is the MonoDroid project. The MonoDroid Project is dependent upon the other projects in the solution.
The problem that I have is that when I use xbuild to build the solution file, I have no way to use the /t:PackageForAndroid target, since it only is valid for the MD Project File.
Currently in Jenkins, I'm doing it like this:
xbuild MyCoolDroidAp/MyCoolDroidApp.sln /p:Configuration=Release /t:Clean
xbuild MyCoolDroidApp/MyCoolDroidApp.sln /p:Configuration=Release /t:Build
xbuild MyCoolDroidApp/MyCoolDroidProject.csproj /p:Configuration=Release /t:PackageForAndroid
This is working, but it seems to me that there should be a way to eliminate the 3rd step. Does anyone have any insight?
You don't need to use Xamarin.Studio/MonoDevelop to sign & zipalign your APK, you can do that at the command line. I've had luck using rake to compile, sign, and zipalign my APK files. Would something like that work for you?
Failing that, here is a simple Powershell script that you could probably port over real easy:
# First clean the Release target.
msbuild.exe HelloWorld.csproj /p:Configuration=Release /t:Clean
# Now build the project, using the Release target.
msbuild.exe HelloWorld.csproj /p:Configuration=Release /t:PackageForAndroid
# At this point there is only the unsigned APK - sign it.
# The script will pause here as jarsigner prompts for the password.
# It is possible to provide they keystore password for jarsigner.exe by adding an extra command line parameter -storepass, for example
# -storepass <MY_SECRET_PASSWORD>
# If this script is to be checked in to source code control then it is not recommended to include the password as part of this script.
& 'C:\Program Files\Java\jdk1.6.0_24\bin\jarsigner.exe' -verbose -sigalg MD5withRSA -digestalg SHA1 -keystore ./xample.keystore -signedjar
./bin/Release/mono.samples.helloworld-signed.apk
./bin/Release/mono.samples.helloworld.apk publishingdoc
# Now zipalign it. The -v parameter tells zipalign to verify the APK afterwards.
& 'C:\Program Files\Android\android-sdk\tools\zipalign.exe' -f -v 4
./bin/Release/mono.samples.helloworld-signed.apk ./helloworld.apk
Hope this helps.
The consensus around the interwebs seems to be that I am doing this the right way.
Regarding Signing your apk i'm using something like this as a part of my makefile and it works ok:
...
BUILD_DIR = ./builds/$(platform)
KEYSTORE_PATH = your_keystore_pass
STORE_PASS = your_keystore_pass
ANDROID_SDK_PATH = path/to/your/android/sdk/dir
#example ANDROID_SDK_PATH = /Developer/AndroidSDK
RES_APK = my_apk.apk
APK_NAME = my_signed_apk.apk
...
sign:
(cd $(BUILD_DIR); jarsigner -verbose -sigalg MD5withRSA -digestalg SHA1 -keystore $(KEYSTORE_PATH) -storepass $(STORE_PASS) result.apk $(STORE_PASS))
(cd $(BUILD_DIR); $(ANDROID_SDK_PATH)/tools/zipalign -v 4 result.apk $(APK_NAME))
(cd $(BUILD_DIR);rm result.apk)
(cd $(BUILD_DIR);rm $(RES_APK))
Hope this helps

Resources