I'm familiar with building large applications using make, but now I have begun using Android Studio and I want to understand how to do things I already do in a Makefile.
Here are a example that might help you frame an answer:
Makefile example: (minimalist)
INK=inkscape
INKFLAGS=--export-area-page
# Resolve *.png dependencies
drawable-mdpi/%.png: %.svg
$(INK) $< --export-png=$# -w 48 -h 48 $(INKFLAGS)
drawable-hdpi/%.png: %.svg
$(INK) $< --export-png=$# -w 72 -h 72 $(INKFLAGS)
drawable-xdpi/%.png: %.svg
$(INK) $< --export-png=$# -w 96 -h 96 $(INKFLAGS)
More simple example:
drawable-mdpi/ic_launcher.png: ic_launcher.svg
inkscape ic_launcher.svg --export-png=ic_launcher.png -w 48 -h 48 --export-area-page
drawable-hdpi/ic_launcher.png: ic_launcher.svg
inkscape ic_launcher.svg --export-png=ic_launcher.png -w 72 -h 72 --export-area-page
How to do that in Gradle?
I want to resolve external dependencies such as mentioned in the above example. Actually I'm doing it via 'make', but i want to completely remove this extra step.
It is possible to run external commands from Grandle and integrate those into your build process. My example runs inkscape.exe on Windows and defines its parameters in the build script, you can also just run a shell script this way.
The following code goes into the app\build.gradle file. Task convertDrawable is written in Groovy syntax and accomplishes the following (tl;dr it is an implementation of your "simple example"):
It looks through all the *.svg files in a custom folder art/drawable
Within each of those *.svg files, looks through all the drawable-* folders in your resources folder
Based on drawable-* folder name, determine the target resolution.
Then calls inkscape.exe to convert each *.svg to *.png with the required size.
Code:
task convertDrawables() {
def ink = 'C:\\Program Files (x86)\\Inkscape\\inkscape.exe'
// look for *.svg files in app/src/art/drawable folder
new File('app\\src\\art\\drawable').eachFileMatch(~/.*\.svg/) { file ->
// look for destination folders
new File('app\\src\\main\\res').eachFileMatch(~/drawable-.*/) { outputDir ->
// define size based on folder name
def size = ''
switch (outputDir.getAbsolutePath()) {
case ~/.*-ldpi/:
size = '36'
break
case ~/.*-mdpi/:
size = '48'
break
case ~/.*-hdpi/:
size = '72'
break
case ~/.*-xhdpi/:
size = '96'
break
case ~/.*-xxhdpi/:
size = '144'
break
case ~/.*-xxxhdpi/:
size = '192'
break
}
def cmd = ink + ' ' + file.getCanonicalPath() + ' --export-png=' + outputDir.getAbsolutePath() + '\\ic_launcher2.png -w ' + size + ' -h ' + size + ' --export-area-page'
def process = cmd.execute();
process.waitFor();
}
}
}
// make sure the convertDrawable task is executed somewhere in the make process
gradle.projectsEvaluated {
preBuild.dependsOn(convertDrawable)
}
Here are the resources I used:
Gradle Doc Chapter 15: More about Tasks
Gradle Doc Chapter 58. Writing Custom Task Classes
Gradle Doc Exec function
Groovy execute()
Stackoverflow Run task before compilation using Android Gradle plugin
Groovy Doc for File
Related
I am trying to build the Alexa auto sdk in Mac OS High Sierra. I am able to successfully build the docker environment required on Mac for the Alexa Auto SDK Builder. While installing the NDK I am getting the following error in terminal `
*******************
*** Docker Mode ***
*******************
NOTE: Run Docker image...
NOTE: SDK Version: 1.2.0
NOTE: Start building for androidarm...
NOTE: Android toolchains will be installed: /workdir/android
NOTE: Checking Android toolchain installation (armeabi-v7a/22)...
NOTE: Installing NDK (android-ndk-r16b)...
NOTE: Downloading file android-ndk-r16b-linux-x86_64.zip
--2018-10-31 05:39:02-- https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip
Resolving dl.google.com (dl.google.com)... 216.58.203.142, 2404:6800:4009:802::200e
Connecting to dl.google.com (dl.google.com)|216.58.203.142|:443... connected.
ERROR: cannot verify dl.google.com's certificate, issued by ‘CN=192.168.86.1’:
Self-signed certificate encountered.
To connect to dl.google.com insecurely, use `--no-check-certificate'.
ERROR: Android toolchain setup failed
I tried wget --no-check-certificate https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip `
and downloaded the NDK but the issue is still persisting. Please help as I am stuck.
We have downloaded Alexa-auto-SDK 1.6.
we used the following steps below. Please update docker, my case everything fine working.
mac$ ./builder/build.sh oe -t androidarm --android-api 26
*******************************************************************************
The scripts provided herein will retrieve several third-party libraries,
environments, and/or other software packages at build-time
("External Dependencies") from third-party sources. These are terms and
conditions that you need to agree to abide by if you choose to build the
External Dependencies. Licenses for the External Dependencies may be found at
builder/README.md. If you do not agree with every term and condition
associated with the External Dependencies, enter “QUIT” in the command line
when prompted by the script.
*******************************************************************************
Type "QUIT" to exit the script now, press ENTER to continue:
\e[1mNOTE\e[0m: Builder only runs within Docker with macOS host
\e[1mNOTE\e[0m: Switching to Docker mode...
*******************
*** Docker Mode ***
*******************
+++ dirname /Users/mac/MetroProject/Android1.5/alexa-auto-sdk/builder/scripts/run-docker.sh
++ cd /Users/mac/MetroProject/Android1.5/alexa-auto-sdk/builder/scripts
++ pwd
+ THISDIR=/Users/mac/MetroProject/Android1.5/alexa-auto-sdk/builder/scripts
+ source /Users/mac/MetroProject/Android1.5/alexa-auto-sdk/builder/scripts/common.sh
++ '[' -z /Users/mac/MetroProject/Android1.5/alexa-auto-sdk/builder ']'
+ VM_HOME=/home/builder
+ IMAGE_REVISION=20190128
+ IMAGE_NAME=aac/ubuntu-base:20190128
+ VOLUME_NAME=buildervolume
+ VOLUME_MOUNT_POINT=/workdir
+ [[ '' != \1 ]]
+ TTY=-t
+ EXTRA_OPTIONS=
+ '[' '!' -z ']'
++ docker images -q aac/ubuntu-base:20190128
+ [[ aad39b28847b == '' ]]
++ docker volume ls
++ grep buildervolume
+ [[ local buildervolume == '' ]]
+ note 'Run Docker image...'
+ echo -e '\e[1mNOTE\e[0m: Run Docker image...'
\e[1mNOTE\e[0m: Run Docker image...
+ execute_command aac/builder/build.sh oe -t androidarm --android-api 26
+ docker run -i -t --rm -v buildervolume:/workdir -v /Users/mac/MetroProject/Android1.5/alexa-auto-sdk/builder/..:/home/builder/aac -e ANDROID_TOOLCHAIN=/workdir/android -e AGL_SDK_BASE=/workdir/agl-sdk -e HOST_PWD=/Users/mac/MetroProject/Android1.5/alexa-auto-sdk -e HOST_SDK_HOME=/Users/mac/MetroProject/Android1.5/alexa-auto-sdk/builder/.. aac/ubuntu-base:20190128 aac/builder/build.sh oe -t androidarm --android-api 26
NOTE: SDK Version: 1.6.0-00002-gb9ee08f-dirty
NOTE: Start building for androidarm...
NOTE: Android toolchains will be installed: /workdir/android
NOTE: Checking Android toolchain installation (armeabi-v7a/26)...
NOTE: Using BUILD_DIR=/workdir/build
Welcome to Alexa Auto Builder!
.c;.
.lKx.
':loooooc' .:c' .c:. .;lkKOocc:. .;looooc,.
.:lc;;;:d00l. ,OKc cK0; 'coOKOocc:. .lOOd:;;cxOk:.
.oKk' ,OKc :0O; .dKx. .oKO; .l00:
.;:::;:dKk' ,OKc :0O; .dKx. ,kKo. 'kKd.
'dOxlc::lkKk' ,OKc :0O; .dKx. ,OKo. .xKd.
.dKk' .lKk' ,OKl. cKO; .dKx. .dKk' :00c
.oKOc...,lkKk' .xKOc'.';cxKO; .oKOl,,,. 'd0kc,..,lO0l.
.:dxxdol;:ol. 'lxkxdol;;lo' .:dxxdo' .;ldxxxxdc'
... ... ....
......
... .',,,;;cc:.
.',,.. .. 'c:.
..,::;,... ...,;;. 'c,
..,:ccc:;,''...... .....'',;:cc;,.. .:,
..',:ccllllcccc:::::::ccccllllc:;'.. ..
...',,;;::::::::;;;,'....
Loading cache: 100% |############################################| Time: 0:00:00
Loaded 1299 entries from dependency cache.
NOTE: Resolving any missing task queue dependencies
NOTE: Multiple providers are available for virtual/nativesdk-libintl (nativesdk-gettext, nativesdk-glibc)
Consider defining a PREFERRED_PROVIDER entry to match virtual/nativesdk-libintl
Build Configuration:
BB_VERSION = "1.36.0"
BUILD_SYS = "x86_64-linux"
NATIVELSBSTRING = "ubuntu-16.04"
TARGET_SYS = "arm-linux-androideabi"
MACHINE = "androidarm"
DISTRO = "aac"
DISTRO_VERSION = "1.6.0"
TUNE_FEATURES = "arm armv7a vfp thumb callconvention-hard"
TARGET_FPU = "hard"
meta-aac-ubuntu = "1.6:b9ee08ff5e9a88e1554d1d90f3e368d92cd302cf"
meta = "HEAD:1b18cdf6b8bdb00ff5df165b9ac7bc2b10c87d57"
meta-aac
meta-aac-builder = "1.6:b9ee08ff5e9a88e1554d1d90f3e368d92cd302cf"
Initialising tasks: 100% |#######################################| Time: 0:00:03
NOTE: Executing SetScene Tasks
NOTE: Executing RunQueue Tasks
NOTE: Tasks Summary: Attempted 553 tasks of which 417 didn't need to be rerun and all succeeded.
Thank you
Trying to build libwally-core C library for Android on Windows in Cygwin with supplied autotools scripts:
libwally-core
After running
bash tools/build_android_libraries.sh
or
bash tools/autogen.sh
I get the following error:
libtoolize: putting auxiliary files in AC_CONFIG_AUX_DIR, 'build-aux'.
libtoolize: copying file 'build-aux/ltmain.sh'
libtoolize: error: AC_CONFIG_MACRO_DIRS([build-aux/m4]) conflicts with
ACLOCAL.AMFLAGS=-I build-aux/m4
autoreconf-2.69: libtoolize failed with exit status: 1
I tried the following things to no avail:
Re-saved all scripts with Unix line-endings (LF only)
Commenting out "ACLOCAL_AMFLAGS = -I tools/build-aux/m4" in Makefile.am
The error happens at the following place in libtool's source in libtoolize.in:
macrodir="$ac_macrodir"
test -z "$macrodir" && macrodir="$am_macrodir"
if test -n "$am_macrodir" && test -n "$ac_macrodir"; then
test "$am_macrodir" = "$ac_macrodir" \
|| func_fatal_error "AC_CONFIG_MACRO_DIR([$ac_macrodir]) conflicts with ACLOCAL_AMFLAGS=-I $am_macrodir."
fi
I assume that the above makes sure that AC_CONFIG_MACRO_DIR and value after "-I" in ACLOCAL_AMFLAGS are identical (checked for identical line endings with hex editor too). The values are identical in both configure.ac and Makefile.am. However, even if I comment out setting ACLOCAL_AMFLAGS in Makefile.am, the error persits.
I would like to compile the library and generate libwallycore.so. Any insight would be much appreciated.
In my previous Windows/Eclipse/ant project development method I had a means of producing a time-limited release apk from the command line. I would like to reproduce this functionality with my new Ubuntu/Android Studio/Gradle setup.
This is how the old system worked:
I had a .bat file which ran a runnable jar which I wrote (and can also create on my new machine). That jar takes two arguments expiry date and where to put the output file. The jar's output is called Timekey.java and it looks like:
package uk.co.myname.timekey;
public final class Timekey{
public static final String EXPIRY_DATE =
"the encrypted string";
public String getExpiryDate() {
return EXPIRY_DATE;
}
}
// Plain date : 2020-01-01.00_00_00
I have my build.xml checking for the presence of this file
<target name="-check-timekey">
<echo>"${timelimit_src}/Timekey.java"</echo>
<available file="${timelimit_src}/Timekey.java" property="timekey.present" />
</target>
and if present it sets the src
<if condition="${timekey.present}">
<src path="${timelimit_src}" />
Thus I can produce an apk which will only run up to the date entered as a parameter to the batch file. The encryption is not military grade but good enough to defeat amateurs and should stop the de-obfuscation fiends.
Any ideas on how to implement this with gradle will be most welcome. I know how to run the jar from a bash script but swapping source directories, just for command line release builds has me stumped
I did manage to solve this in the end. I created the build type 'release' which was not previously necessary,as 'main' sufficed. I also reated the build type 'timelimited'. Timekey.java was removed from main and placed in debug, release and timelimited's src/java folders (including the class stucture hierarchical folders).
This script completed the process
#!/bin/bash
# Script to build time limited apk NBT 2nd March 2016
# Must be run from one leve below AndroidStudioProjects folder
# Must have one argument of expiry date in YYYY-MM-DD format
CURRENT_DIR=`pwd`
case "$CURRENT_DIR" in
*AndroidStudioProjects/*) ;;
*) echo "Quitting because of wrong starting directory name"
echo "You must be one folder below ~/AndroidStudioProjects to run this script"
exit 1 ;;
esac
OUTDIR_SUFFIX=/app/src/timelimited/java/uk/co/myname/timekey/
OUTDIR=$CURRENT_DIR$OUTDIR_SUFFIX
echo "Directory to write in: "$OUTDIR
if [ -z "$1" ]
then
echo "argument 1 required, as expiry date in YYYY-MM-DD format, so quitting"
exit 1
fi
EXDATE=$1
case "$EXDATE" in
????-??-??) ;;
*) echo "Quitting because of bad date format on parameter 1"
exit 1 ;;
esac
# the encrypt5.jar requires these two arguments
java -jar ~/runnablejars/encrypt5.jar $EXDATE $OUTDIR
echo " "
echo " "
gradlew assembleTimelimited
cat $OUTDIR/Timekey.java
echo " "
echo "All done, now IF you saw Timekey.java printed out, then"
echo "the time limited apk is built. Run gradlew InstallTimelimited"
echo "to install it on a running device"
while true; do
read -p "Do you wish to install this apk on running device y/n [enter]? " yn
case $yn in
[Yy]* ) gradlew InstallTimelimited; break;;
[Nn]* ) exit;;
* ) echo "Please answer yes or no.";;
esac
done
I hope this might prove useful. There are probably less convoluted ways of doing it but I don't know one.
when I executed the command ' staprun ' on the android , I met following problem :
# /data/systemtap-1.0-omap/bin/staprun /data/local/msyscall_all.ko
/data/systemtap-1.0-omap/bin/staprun: 1: Syntax error: "(" unexpected
#
the module msyscall_all.ko is cross-compiled with the kernel that my android run on.
firstly, I am sure that msyscall_all.ko is exactlly right , because I use command ' insomd ' and ' lsmod ' to ensure it is right .I did it as following :
# lsmod
omaplfb 8986 0 - Live 0xbf032000
pvrsrvkm 137346 29 omaplfb, Live 0xbf000000
# insmod /data/local/msyscall_all.ko
# lsmod
msyscall_all 1121778 0 - Live 0xbf037000
omaplfb 8986 0 - Live 0xbf032000
pvrsrvkm 137346 29 omaplfb, Live 0xbf000000
#
secondly, I followed the instruction in :http://omappedia.org/wiki/Systemtap#Systemtap_1.0_code_update_for_OMAP_ARM_platforms
And my systemtap-1.0-omap was cross-compiled successfully!! Howerver, after I used 'adb push' to pushed the systemtap-1.0-omap to my android device and ran it ,I got the error below :
/data/systemtap-1.0-omap/bin/staprun: 1: Syntax error: "(" unexpected
when the 'staprun' was ran on the ubuntu ,it gave me advice how run ' staprun ' , and when it was run on the android device ,it went wrong :
# /data/systemtap-1.0-omap/bin/staprun
/data/systemtap-1.0-omap/bin/staprun: 1: Syntax error: "(" unexpected
# exit
ubuntu#ubuntu:~$ cd systemtap-1.0-omap/bin/
ubuntu#ubuntu:~/systemtap-1.0-omap/bin$ ./staprun
ERROR: Need a module name or path to load.
./staprun [-v] [-c cmd ] [-x pid] [-u user] [-A|-L|-d]
[-b bufsize] [-o FILE [-D] [-S size[,N]]] MODULE [module-options]
-v Increase verbosity.
-c cmd Command 'cmd' will be run and staprun will
exit when it does. The '_stp_target' variable
will contain the pid for the command.
-x pid Sets the '_stp_target' variable to pid.
-o FILE Send output to FILE. This supports strftime(3)
formats for FILE.
-b buffer size The systemtap module specifies a buffer size.
Setting one here will override that value. The
value should be an integer between 1 and 4095
which be assumed to be the buffer size in MB.
That value will be per-cpu in bulk mode.
-L Load module and start probes, then detach.
-A Attach to loaded systemtap module.
-d Delete a module. Only detached or unused modules
the user has permission to access will be deleted. Use "*"
(quoted) to delete all unused modules.
-D Run in background. This requires '-o' option.
-S size[,N] Switches output file to next file when the size
of file reaches the specified size. The value
should be an integer greater than 1 which is
assumed to be the maximum file size in MB.
When the number of output files reaches N, it
switches to the first output file. You can omit
the second argument.
MODULE can be either a module name or a module path. If a
module name is used, it is looked for in the following
directory: /lib/modules/`uname -r`/systemtap
ubuntu#ubuntu:~/systemtap-1.0-omap/bin$
I have tried my best , but I can not solve it !!
I'm compiling my (fairly simple, just 5 files with few hundred LOC) app from command line on OSX using:
ant debug
It works. But it works slowly:
BUILD SUCCESSFUL
Total time:
26 seconds
Why is that? It takes this much time even if I change only one line in one java file. Most of this time is spent in dex stage (about 20 seconds), which is AFAIK creating Dalvik bytecode. But my friend that also works on the same project on Windows using Eclipse says that compiling takes only a second or two on his machine. Is there anything I can do to speed up this proccess?
I finally found a solution for this! It's a bit of a hack, but it works.
First, go to your ANDROID-SDK/platform-tools directory, then rename dx app to something else, like dextool, and finally create new dx file with contents:
#!/bin/sh
shift
dextool --dex --incremental --no-optimize $#
Replace "dextool" with the name you chose before. This will prepend (undocumented) --incremental attribute to every dex invocation, which will massively decrease build times by dexing only classes that have changed between builds. Now it looks like this:
[dx] Merged dex A (1 defs/11,3KiB) with dex B (359 defs/1253,2KiB). Result is 359 defs/1519,3KiB. Took 0,5s
0.5s instead of 20s is a huge difference!
Edit - few remarks:
you have to compile your project at least once before using this, because it uses previous classes.dex file
you can run into problems when using other Android toolchains than ant
UPDATE:
Google released SDK Tools 21.0, which renders above tweak absolete, because it does supports pre-dexing. Finally!
Even in 21.1.1 with the --incremental --no-optimize added in the original dex.bat it is slow, so I went on to figure something out, the result is: if you order the .jar files passed to dex by size you get better performance.
Watch https://code.google.com/p/android/issues/detail?id=79166 for updates, I hope they agree and this goes into vNext.
#!/usr/bin/perl
use strict;
use warnings;
#use Data::Dump qw(dump);
use List::Util qw(first), qw(sum);
# size of the argument, -s for files, -s on **/*.class for folders
sub size {
if (-d $_) {
# directory size is sum of all class files in the dir recursively
# account for pre-dexing and compression with a 25% decrease
return sum(map { size($_) * 0.25 } <$_/*.class>) || 0;
}
return -s $_; # use built-in size operator
}
my $dx_args_with_args =
qr/^--(output|positions|(no-)?optimize-list|dump-(to|width|method)|num-threads|main-dex-list|input-list)$/;
my $nArgs = $#ARGV;
# last argument like --blah, those are for dx
my $lastArg = $nArgs - first { $ARGV[$nArgs - $_] =~ /^--/ } 0..$nArgs;
if ($lastArg != -1 && $ARGV[$lastArg] =~ /$dx_args_with_args/) {
$lastArg += 1;
}
my #inputs = map { $_->[1] }
sort { $a->[0] <=> $b->[0] }
map { [size(), $_] }
#ARGV[$lastArg + 1 .. $nArgs];
print join(" ", #ARGV[0..$lastArg], #inputs);
exit 0;
Usage
have Perl on your path
copy the above perl script to ANDROID-SDK/build-tools/v.v.v/dx.pl
rename dx in ANDROID-SDK/build-tools/v.v.v/
Unix: rename dx to dx-orig
Windows: rename dx.bat to dx-orig.bat
add a new replacement dx which calls through:
Windows: dx.bat
#echo off
setlocal
set args=%*
for /f "delims=" %%i in ('perl "%~dp0dx.pl" %args%') do set args=%%i
call "%~dp0dx-orig.bat" %args%
endlocal
Unix: dx
#!/bin/sh
dx-orig `perl dx.pl $#`