-
Notifications
You must be signed in to change notification settings - Fork 11
build.pl
quillon edited this page Jan 27, 2016
·
1 revision
###build.pl
#!/usr/bin/env perl
use strict; ### 限制,比如变量的作用域等
use warnings; ### 增加警告信息
### qw表示列表
### FindBin用来获取脚本运行的路径,路径存放在$Bin中
use FindBin qw($Bin);
use lib $Bin;
### 使用Xposed模块,即Xposed.pm
use Xposed;
### 表示导入模块Archive::Zip,冒号表示导入一“组”函数,而不是向下面说明中的一个
use Archive::Zip qw(:ERROR_CODES :CONSTANTS);
use Data::Dumper;
use File::Basename;
use File::Copy;
### 表示导入模块File::Path,并且只导入make_path和remove_tree这两个函数名,其他的函数名
### 并不导入,但是仍然可以通过全名的方式引用,比如:File::Path::make_path
use File::Path qw(make_path remove_tree);
### 用来获取命令行参数
use Getopt::Std;
use POSIX qw(strftime);
use Tie::IxHash;
use Term::ANSIColor;
###定义变量VERSION
###our定义包全局变量。相当于定义全局变量吧
our $VERSION = '0.9';
### my用来限定作用域,这里表示本文件内。子程序内用my表示在本子程序内,等等。相当于c++中的局部变量
### %用来表示一个哈系表,这里定义了一个opts的哈系表
my %opts;
$| = 1; ### 取消I/O操作的缓冲,改为直接进行I/O?
# Main routine
sub main() {
$Getopt::Std::STANDARD_HELP_VERSION = 1;
### 获取-a -f -i -s -t -v这些开关,并存入%opts哈希表中。
### 其中,后面跟‘:’表示后面要跟参数。a s t v要有参数,f i v不需要参数,
### 不需要参数的,若命令行中有此开关,则其对应的值为1,否则为0。
getopts('a:fis:t:v', \%opts) || usage(2);
### 调用Xposed模块的load_config方法,获取build.conf文件中的配置信息
# Load the config file
print_status("Loading config file $Bin/build.conf...", 0);
$Xposed::cfg = Xposed::load_config("$Bin/build.conf") || exit 1;
### 检查必要信息
# Check some build requirements
print_status('Checking requirements...', 0);
Xposed::check_requirements() || exit 1;
### 获取命令行-a的参数,默认是build
my $action = $opts{'a'} || 'build';
### 确认编译目标
# Determine build targets
my @targets;
if ($action eq 'build' || $action eq 'prunelogs') {
### 获取命令行-t的参数,默认值为空
my $target_spec = $opts{'t'} || '';
print_status("Expanding targets from '$target_spec'...", 0);
### 获取build.conf中的目标列表
@targets = Xposed::expand_targets($target_spec, 1);
if (!@targets) {
print_error('No valid targets specified');
usage(2);
}
print "\n";
}
### 当动作是编译的时候
if ($action eq 'build') {
### 编译时不支持-f 开关,屏蔽
# Check whether flashing is possible
if ($opts{'f'} && $#targets != 0) {
print_error('Flashing is only supported for a single target!');
exit 1;
}
### 为每一个目标platform和sdk组合执行all_in_one
# Build the specified targets
foreach my $target (@targets) {
all_in_one($target->{'platform'}, $target->{'sdk'}, !$opts{'v'}) || exit 1;
}
} elsif ($action eq 'prunelogs') {
# Remove old logs
foreach my $target (@targets) {
prune_logs($target->{'platform'}, $target->{'sdk'});
}
} else {
print_error("Unknown action specified: $action");
usage(2);
}
print_status('Done!', 0);
}
# Print usage and exit
sub usage($) {
my $exit = shift;
print STDERR <<USAGE;
This script helps to compile and package the Xposed executables and libraries.
Usage: $0 [-v] [-i] [-f] [-a <action>][-t <targets>] [-s <steps>]
-a Execute <action>. The default is "build".
-f Flash the files after building and performs a soft reboot. Requires step "zip".
-t Build for targets specified in <targets>.
-s Limit build steps to <steps>. By default, all steps are performed.
-i Incremental build. Compile faster by skipping dependencies (like mm/mmm).
-v Verbose mode. Display the build log instead of redirecting it to a file.
Possible actions are:
build Builds the native executables and libraries.
prunelogs Removes logs which are older than 24 hours.
Format of <targets> is: <platform>:<sdk>[/<platform2>:<sdk2>/...]
<platform> is a comma-separated list of: arm, x86, arm64 (and up to SDK 17, also armv5)
<sdk> is a comma-separated list of integers (e.g. 21 for Android 5.0)
Both platform and SDK accept the wildcard "all".
Values for <steps> are provided as a comma-separated list of:
compile Compile executables and libraries.
collect Collect compiled files and put them in the output directory.
prop Create the xposed.prop file.
zip Create the flashable ZIP file.
Examples:
$0 -t arm:all/x86,arm64:21
(build ARM files for all SDKs, plus x86 and arm64 files for SDK 21)
USAGE
exit $exit if $exit >= 0;
}
sub HELP_MESSAGE() {
usage(-1);
}
sub VERSION_MESSAGE() {
print "Xposed build script, version $VERSION\n";
}
### 检查是否需要执行指定的步骤,通过命令行开关-s的参数判断
# Returns whether a certain build step should be performed
sub should_perform_step($) {
my $step = shift;
return 1 if !$opts{'s'};
my @steps = split(m/[, ]+/, $opts{'s'});
foreach (@steps) {
return 1 if $step eq $_;
}
return 0;
}
### 为给定的platform/SDK组合执行编译操作
# Performs all build steps for one platform/SDK combination
sub all_in_one($$;$) {
my $platform = shift;
my $sdk = shift;
my $silent = shift || 0;
print_status("Processing SDK $sdk, platform $platform...", 0);
### 执行编译,失败的话返回0
compile($platform, $sdk, $silent) || return 0;
### 如果不是host和hostd
if ($platform ne 'host' && $platform ne 'hostd') {
### 执行cellect,失败的话返回0(将aosp编译出来的xposed相关的文件,复制到build.conf中定义的输出目录中)
collect($platform, $sdk) || return 0;
### 创建xposed.prop文件,失败的话返回0
create_xposed_prop($platform, $sdk, !$silent) || return 0;
### 打包zip文件,失败的话返回0
create_zip($platform, $sdk) || return 0;
}
print "\n\n";
return 1;
}
# Compile Xposed (and possibly ART) for one SDK/platform
sub compile($$;$) {
my $platform = shift;
my $sdk = shift;
my $silent = shift || 0;
### 检查是否需要执行当前步骤
should_perform_step('compile') || return 1;
print_status("Compiling...", 1);
### 获取make参数
my @params = Xposed::get_make_parameters($platform, $sdk);
### 定义目标列表
my @targets = qw(xposed);
### 定义makefile列表
my @makefiles = qw(frameworks/base/cmds/xposed/Android.mk);
# Version-specific targets
### 如果sdk小于21,目标增加dalvik
if ($sdk < 21) {
push @targets, qw/libxposed_dalvik/;
### 否这增加art相关的内容
} else {
push @targets, qw/libxposed_art/;
push @targets, qw/libart libart-compiler libart-disassembler libsigchain/;
push @targets, qw/dex2oat oatdump patchoat/;
push @makefiles, qw(art/Android.mk);
}
### host和hostd相关
if ($platform eq 'host') {
$ENV{'HOST_PREFER_32_BIT'} = 'true';
$ENV{'ART_BUILD_HOST_NDEBUG'} = 'true';
@targets = qw(
out/host/linux-x86/bin/dex2oat
out/host/linux-x86/bin/oatdump
);
@makefiles = qw(art/Android.mk);
} elsif ($platform eq 'hostd') {
$ENV{'HOST_PREFER_32_BIT'} = 'true';
$ENV{'ART_BUILD_HOST_DEBUG'} = 'true';
@targets = qw(
out/host/linux-x86/bin/dex2oatd
out/host/linux-x86/bin/oatdumpd
);
@makefiles = qw(art/Android.mk);
}
### 调用Xposed的compile编译
my $result = Xposed::compile($platform, $sdk, \@params, \@targets, \@makefiles, $opts{'i'}, $silent);
delete($ENV{'ART_BUILD_HOST_NDEBUG'});
delete($ENV{'ART_BUILD_HOST_DEBUG'});
return $result;
}
# Collect final files into a single directory
sub collect($$) {
my $platform = shift;
my $sdk = shift;
### 检查是否需要执行当前步骤
should_perform_step('collect') || return 1;
print_status("Collecting compiled files...", 1);
### 获取收集目录(最终的输出文件保存目录)
my $coldir = Xposed::get_collection_dir($platform, $sdk);
make_path($coldir);
### 获取build.conf中定义的aosp的路径
my $rootdir = Xposed::get_rootdir($sdk) || return 0;
### 获取aosp的输出目录,相对于root的
my $outdir = Xposed::get_outdir($platform) || return 0;
### 清空收集目录
# Clear collection directory
remove_tree($coldir . '/files');
return 0 if -e $coldir . '/files';
# Copy files
### 获取编译出来的文件的哈希表
my $files = get_compiled_files($platform, $sdk);
### 复制文件
while( my ($file, $target) = each(%$files)) {
$file = $rootdir . '/' . $outdir . $file;
$target = $coldir . '/files' . $target;
print "$file => $target\n";
make_path(dirname($target));
if (!copy($file, $target)) {
print_error("Copy failed: $!");
return 0;
}
}
return 1;
}
### 获取编译出来的文件的哈希表
# Returns a hash paths of compiled files
sub get_compiled_files($$) {
my $platform = shift;
my $sdk = shift;
my %files;
tie(%files, 'Tie::IxHash');
if ($sdk < 21) {
$files{'/system/bin/app_process_xposed'} = '/system/bin/app_process_xposed';
$files{$_} = $_ foreach qw(
/system/lib/libxposed_dalvik.so
);
} else {
$files{$_} = $_ foreach qw(
/system/bin/app_process32_xposed
/system/lib/libxposed_art.so
/system/lib/libart.so
/system/lib/libart-compiler.so
/system/lib/libart-disassembler.so
/system/lib/libsigchain.so
/system/bin/dex2oat
/system/bin/oatdump
/system/bin/patchoat
);
}
if ($platform eq 'arm64') {
# libart-disassembler is required by oatdump only, which is a 64-bit executable
delete $files{'/system/lib/libart-disassembler.so'};
$files{$_} = $_ foreach qw(
/system/bin/app_process64_xposed
/system/lib64/libxposed_art.so
/system/lib64/libart.so
/system/lib64/libart-disassembler.so
/system/lib64/libsigchain.so
);
}
return \%files;
}
### 创建xposed.prop文件
# Creates the /system/xposed.prop file
sub create_xposed_prop($$;$) {
my $platform = shift;
my $sdk = shift;
my $print = shift || 0;
should_perform_step('prop') || return 1;
print_status("Creating xposed.prop file...", 1);
# Open the file
my $coldir = Xposed::get_collection_dir($platform, $sdk);
my $propfile = $coldir . '/files/system/xposed.prop';
print "$propfile\n";
make_path(dirname($propfile));
if (!open(PROPFILE, '>', $propfile)) {
print_error("Could not write to $propfile: $!");
return 0;
}
# Prepare variables
my $version = $Xposed::cfg->val('Build', 'version');
$version = sprintf($version, strftime('%Y%m%d', localtime())) if $version =~ m/%s/;
if ($platform eq 'armv5') {
$platform = 'arm';
}
# Calculate minimum / maximum compatible SDK versions
my $minsdk = $sdk;
my $maxsdk = $sdk;
if ($sdk >= 15 && $sdk <= 19) {
$minsdk = 15;
$maxsdk = 19;
}
# Write to file
my $content = <<EOF;
version=$version
arch=$platform
minsdk=$minsdk
maxsdk=$maxsdk
EOF
print PROPFILE $content;
print $content if $print;
# Close the file
close(PROPFILE);
return 1;
}
### 创建升级用的zip包
# Create a flashable ZIP file with the compiled and some static files
sub create_zip($$) {
my $platform = shift;
my $sdk = shift;
should_perform_step('zip') || return 1;
print_status("Creating flashable ZIP file...", 1);
# Create a new ZIP file
my $zip = Archive::Zip->new();
my $outdir = $Xposed::cfg->val('General', 'outdir');
my $coldir = Xposed::get_collection_dir($platform, $sdk);
make_path($coldir);
$zip->addTree($coldir . '/files/', '') == AZ_OK || return 0;
$zip->addDirectory('system/framework/') || return 0;
$zip->addFile("$outdir/java/XposedBridge.jar", 'system/framework/XposedBridge.jar') || return 0;
# TODO: We probably need different files for older releases
$zip->addTree($Bin . '/zipstatic/_all/', '') == AZ_OK || return 0;
$zip->addTree($Bin . '/zipstatic/' . $platform . '/', '') == AZ_OK || return 0;
# Set last modification time to "now"
my $now = time();
foreach my $member($zip->members()) {
$member->setLastModFileDateTimeFromUnix($now);
}
# Write the ZIP file to disk
$Xposed::cfg->val('Build', 'version') =~ m/^(\d+)(.*)/;
my ($version, $suffix) = ($1, $2);
if ($suffix) {
$suffix = sprintf($suffix, strftime('%Y%m%d', localtime()));
$suffix =~ s/[\s\/|*"?<:>%()]+/-/g;
$suffix =~ s/-{2,}/-/g;
$suffix =~ s/^-|-$//g;
$suffix = '-' . $suffix if $suffix;
}
my $zipname = sprintf('%s/xposed-v%d-sdk%d-%s%s.zip', $coldir, $version, $sdk, $platform, $suffix);
print "$zipname\n";
$zip->writeToFileNamed($zipname) == AZ_OK || return 0;
Xposed::sign_zip($zipname);
# Create a stable symlink to the latest version
my $latest = $coldir . '/latest.zip';
unlink($latest);
if (!symlink(basename($zipname), $latest)) {
print_error("Could not create link to latest version: $!");
}
# Flash the file (if requested)
if ($opts{'f'}) {
print_status("Flashing ZIP file...", 1);
system("adb push $zipname /data/local/tmp/xposed.zip") == 0 || return 0;
system("adb push $Bin/zipstatic/$platform/META-INF/com/google/android/update-binary /data/local/tmp/update-binary") == 0 || return 0;
system("adb shell 'chmod 700 /data/local/tmp/update-binary'") == 0 || return 0;
system("adb shell su -c 'NO_UIPRINT=1 /data/local/tmp/update-binary 2 1 /data/local/tmp/xposed.zip'") == 0 || return 0;
system("adb shell 'rm /data/local/tmp/update-binary /data/local/tmp/xposed.zip'") == 0 || return 0;
system("adb shell su -c 'stop; sleep 2; start'") == 0 || return 0;
}
return 1;
}
# Remove old logs
sub prune_logs($$) {
my $platform = shift;
my $sdk = shift;
my $cutoff = shift || (time() - 86400);
my $logdir = Xposed::get_collection_dir($platform, $sdk) . '/logs';
return if !-d $logdir;
print_status("Cleaning $logdir...", 1);
opendir(DIR, $logdir) || return;
foreach my $file (sort readdir(DIR)) {
next if ($file !~ m/\.log$/);
my $filepath = $logdir . '/' . $file;
my $modtime = (stat($filepath))[9];
if ($modtime < $cutoff) {
print "[REMOVE] $file\n";
unlink($filepath);
} else {
print "[KEEP] $file\n";
}
}
closedir(DIR);
print "\n";
}
@main();