Matt Reach

使用 Rake 重写打包脚本

2017-06-04

我最初的博客系统使用的是 Octopress,创建博客或者预览博客都是使用的 rake 命令,感觉很是方便,现在虽然直接使用了 Jekyll,但是这套命令还是被我移植过来继续使用着。之前我用 Shell 编写过一套打包的脚本,现在接触了 Ruby 脚本,对比之下感觉 Shell 这门语言太不友好了,编程效率很低,于是前天晚上开始学习 rake,边写边学边查资料,一步步的重写了这套打包脚本,故趁热打铁写下这篇博客记录用到的知识点。

Ruby 语言的图标:

名词解释

学习 Ruby 之前先捋顺下相关的名词吧

  • Ruby: 是一门纯粹的面向对象编程语言,她语法简洁优美,简单易学,兼具函数式编程和命令式编程特色,由日本的松本行弘创建,松本行弘被称为 Matz 。这是 Ruby 中文官网: http://www.ruby-lang.org/zh_cn/

  • RVM: 用来管理 Ruby 版本及相关插件,方便切换版本环境。

  • Ruby on Rails: 开发 web 应用的框架。

  • Gem: 组织 Ruby 程序或者代码类库的包,每个 Ruby 应用程序就是一个 gem 包。

  • RubyGems: 方便而强大的 Ruby 程序包管理器,用来管理 gem 的,类似 RedHat 的 RPM;Ruby环境里已经包含了 RubyGems 了,无需额外安装。

  • Rake: 如果你已经查看了所有安装的 gem 包的话,你会发现其中一个就是 rake (10.5.0, 10.4.2),这个是很重要的一个 gem。跟 Make 类似,是一个 DSL 构建语言,Rails 有很多任务常用 rake 来完成。

  • Rakefile: 使用 Ruby 语言按照 DSL 格式编写的文件,定义了 Rake 的执行过程。

Ruby 基础知识

编写 Rakefile 必须知道 Ruby 的语法才行,简单学习下吧:

  • 特殊变量的命名规则

    • 常量: 以大写字母开头的变量,如: Jekyll,为方便起见常量全部大写
    • 全局变量: 以”$”开头的变量,如: $jekyll
    • 实例变量: 以”@”开头的变量,如: @jekyll
    • 类变量: 以”@@”开头的变量,如: @@jekyll

      我最初不知道大写字母开头的是常量,原本想的是使用全局变量的,结果改变了常量的值,执行时发现了警告:
      warning: already initialized constant JEKYLL
      所以最好不要修改常量的值!

    • __FILE__: 当前源文件的名称(含路径)

    • __LINE__: 当前行在源代码中的编号
  • 注释

    • 单行注释: ‘#单行注释’
    • 多行注释:

      1
      2
      3
      4
      5
      =begin
      多行注释
      多行注释
      多行注释
      =end
  • 输出日志: puts “log something”,跟 printf 类似

  • 数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ary = ["abc",19,3.14,"This is my pubby"]
    # 等同于
    arr = Array.new
    arr[0] = "abc"
    arr[1] = 19
    arr[2] = 3.14
    arr[3] = "This is my pubby"
    # 遍历数组
    ary.each do |i|
    puts i
    end
  • Hash,OC 里叫字典

    1
    2
    3
    4
    5
    colors = {"red" => 0xff0000, "green" => 0x00ff00,"blue" => 0x0000ff}
    # 遍历 Hash
    colors.each do |key,value|
    print key," is ",value, "\n"
    end
  • Sequence 序列

    1
    2
    3
    4
    seq = (10..15)
    seq.each do |n|
    print n,' '
    end
  • 字符串: “Hello World”

  • 特殊语句块

    1
    2
    3
    4
    5
    6
    7
    BEGIN{
    puts "【执行其他代码前,先执行BEGIN块里的语句!】"
    }

    END{
    puts "【执行完其他所有代码后,再执行END块里的语句!】"
    }
  • 方法

    1
    2
    3
    def test(content)
    puts "### " + content
    end

Rake 入门

  • Rakefile

    先创建名为 Rakefile 的文件,然后就可以按照格式写了:

    1
    2
    3
    4
    desc "task 说明"
    task :test do
    puts "I'm a task"
    end

    在 Rakefile 目录打开终端并执行: rake test,你会发现测试语句输出了;这就是 Rake 的模板,可以通过 rake -T 查看定义的所有 task,desc 会作为文档显示出来:

    1
    2
    3
    4
    xuqianlong$ rake test
    I'm a task
    xuqianlong$ rake -T
    rake test # task 说明
  • namespace

    你可能会定义多个 task,这样一来可能会不好命名或者冲突,辛好可以通过 namespace 来解决,如果有命名空间的话,执行的时候带上才行,比如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 添加到 Rakefile 里
    namespace :TestNamespace do
    desc "test namespace 说明"
    task :test do
    puts "I have a namespace."
    end
    end
    # 命令行继续执行
    xuqianlong$ rake -T
    rake TestNamespace:test # test namespace 说明
    rake test # task 说明
    xuqianlong$ rake TestNamespace:test
    I have a namespace.
  • default task

    可以在 Rakefile 里指定默认的 task,然后执行时就不用带上 task 名字了

    1
    2
    3
    4
    5
    # 添加到 Rakefile 里
    task :default => [:test]
    # 调用
    xuqianlong$ rake
    I'm a task
  • import

    为了减轻 Rakefile 的压力,可以把代码分散到不同的 rake 文件里,然后使用 import 导入即可,可以包含 task 任务,也可以包含 ruby 代码哦;
    buildConfig.rake 里放了打包的先关配置信息,放在config目录下,这样导入就行了:

    1
    import("config/buildConfig.rake")
  • invoke

    一个 task 可以调用别的 task,代码如下:

    1
    2
    # PackageSDK 是命名空间, prepareBuildFolder 是 task 的名字
    Rake::Task["PackageSDK:prepareBuildFolder"].invoke

Call Shell

打包的命令是 Shell 命令,所以要想办法让 ruby 去调用才行,查到了一下方法:

  • 反引号 (Backticks) : `cmd`

    1
    2
    3
    # 可以获取到命令的返回值
    xcversion = `xcodebuild -version`
    puts xcversion
  • system “cmd”

    1
    2
    3
    4
    5
    # Hi 会被直接输出到命令行
    system "echo Hi"
    # 可以通过 $? 获取子进程 pid 和执行结果
    pid 48526 exit 0
    # 可以 $?.to_i 获取执行结果,如果是 0 这说明正常执行

YAML

在 Ruby 的世界里,他使用很广泛,可读性很好,用来取代 XML 标记语言,比 XML 更有优势;一般用作配置文件,因此之前用 shell 写的配置文件就改为 .yml 文件了:

1
2
3
4
DESC: "SohuLiveSDK for Video"
SCHEME_NAME_ARR: ["SohuCoreFoundation","SohuOneSDK","SohuGameSDK","SohuLiveSDK-Video"]
PLIST_INFO: "SohuLiveSDK/SohuLiveSDK/video-info.plist"
CATEGORY_NAME: "千帆SDK-Video"

读取配置文件内容:

1
2
# 读出来后是 HASH 类型,很方便使用
config = YAML::load(File.read(cfgdir, cfgname)))

使用前需要导入库哦:

1
require 'yaml'

STDIN

所有的打包渠道都放在 PLACE_CONF_ARR 数组中,使用者需要选择打哪个渠道,因此须要求输入 1 ~ N 之间的数字,其中 N = PLACE_CONF_ARR.size,代表打包渠道数组长度,代码如下

1
2
3
4
5
input = 0
until (input > 0) && (input <= PLACE_CONF_ARR.size)
puts "请输入1~" + PLACE_CONF_ARR.size.to_s + "之间的数字:"
input = $stdin.gets.chomp().to_i
end

Installed xx Gem ?

我使用了 xcpretty 将 xcodebuild 的输出信息格式化,但是同事的电脑可能没装 xcpretty,因此要判断出来是否安装了,如果没安装就不使用,这样程序才算健壮,逻辑为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#定义一个全局变量
$g_xcpretty = false
# 判断下是否安装了 xcpretty
isInstalled = `gem list -i xcpretty`
# 去掉末尾的换行符
isInstalled = isInstalled.chomp();
# 如果是 "true" 则表明安装了
if isInstalled == "true"
$g_xcpretty = true
end

# ...

# 剩下的逻辑通过 $g_xcpretty 全局变量判断即可
def build_sdk_scheme(sdk,scheme)
puts "========== Build #{scheme} On #{sdk} Platform =========="
if $g_xcpretty
system "/usr/bin/xcodebuild -workspace #{wksp} -scheme #{scheme} -configuration #{CONFIGURATION} -sdk #{sdk} BUILD_DIR=#{build_dir}" | xcpretty
else
system "/usr/bin/xcodebuild -workspace #{wksp} -scheme #{scheme} -configuration #{CONFIGURATION} -sdk #{sdk} BUILD_DIR=#{build_dir}"
end
end
Tags: Script
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章