Skip to content

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();

Clone this wiki locally