APPRTC分析系列0--grunt过程分析0

一、grunt工具分析

apprtc的编译步骤为:
npm install
grunt build

网上查阅资料,发现grunt为一种构建项目的方式,其基本介绍为
http://www.gruntjs.net/getting-started
通过该介绍可以发现,grunt是通过npm安装和管理的,而npm又是node.js的包管理器。

在编译apprtc之前,需要安装的grunt-cli为Grunt命令行。执行的npm install -g grunt-cli为将命令行安装到全局环境中,其中的-g参数代表global全局的意思,这是npm install的参数。
安装完grunt-cli后,grunt命令就被加入到系统路径中了。

二、grunt过程分析

1、npm install
在apprtc目录下面执行npm install时,npm包管理器读取apprtc目录下面的package.json中的内容,并安装必要的包。
如,apprtc目录下的package.json内容如下

{
  "name": "apprtc",
  "version": "1.0.0",
  "description": "Project checking for AppRTC repo",
  "license": "BSD-3-Clause",
  "main": "Gruntfile.js",
  "repository": {
    "type": "git",
    "url": "https://github.com/webrtc/apprtc.git"
  },
  "scripts": {
    "test": "grunt --verbose"
  },
  "devDependencies": {
    "grunt": "^0.4.0",
    "grunt-cli": ">=0.1.9",
    "grunt-closurecompiler": ">=0.0.21",
    "grunt-contrib-compress": "^0.13.0",
    "grunt-contrib-csslint": ">=0.3.1",
    "grunt-contrib-jshint": ">=0.10.0",
    "grunt-htmlhint": ">=0.9.12",
    "grunt-jinja": ">=0.3.0",
    "grunt-jscs": ">=0.8.1",
    "grunt-jstestdriver-phantomjs": ">=0.0.7",
    "grunt-shell": "^1.1.1",
    "socket.io-client": "^1.2.0",
    "webrtc-adapter": "^1.0.6"
  }
}

猜测npm install会安装package.json中的devDependencies对应的值,即各种必要的依赖,包括一些grunt插件。这些插件将会在后面执行grunt命令时使用。

npm install执行完之后,会在apprtc目录下面生成node_modules目录,该目录下面是npm intall安装的node.js包。查看node_modules目录下内容,发现和devDependencies值列表相同。证明刚才的npm install命令是执行安装package.json中devDependencies的包列表。

也可以通过在apprtc下面,执行npm list命令查看该目录下安装的nodejs包的结构。

2、grunt build
apprtc目录中存在一个Gruntfile.js的文件。里面涉及到grunt命令时的一系列东东。其介绍也在http://www.gruntjs.net/getting-started中。有兴趣的可以自己看一下。我们依据上面链接提供的知识来分析一下grunt build到底执行了什么。

看一下文件内容中的

 grunt.registerTask('default', ['csslint', 'htmlhint', 'jscs', 'jshint',
                                 'runPythonTests', 'shell:genJsEnums', 'jstests']);        
  grunt.registerTask('travis', ['shell:getPythonTestDeps',
                                'shell:installPythonTestDepsOnLinux',
                                'default']);
  grunt.registerTask('runPythonTests', ['shell:buildAppEnginePackageWithTests',
                                        'shell:getPythonTestDeps',
                                        'shell:runPythonTests',
                                        'shell:removePythonTestsFromOutAppEngineDir']);
  grunt.registerTask('jstests', ['shell:genJsEnums', 'closurecompiler:debug', 'grunt-chrome-build', 'jstdPhantom']);
  // buildAppEnginePackage must be done before closurecompiler since buildAppEnginePackage resets out/app_engine.

  grunt.registerTask('build', [`'shell:buildAppEnginePackage', 'shell:genJsEnums', 'closurecompiler:debug', 'grunt-chrome-build'`]);

grunt.registerTask是一个grunt的api,其原型为grunt.registerTask(taskname,tasklist).其中tasklist必须是一个任务数组。其中的taskname为一个任务别名。在命令行执行grunt taskname时,会依次执行tasklist中的任务。

实际上grunt build是在执行Gruntfile.js文件中的build任务。而我们找一下,发现build任务是一个任务列表的别名。
按照文件规定,其会依次执行

'shell:buildAppEnginePackage', 
'shell:genJsEnums', 
'closurecompiler:debug', 
'grunt-chrome-build'

这4个任务。
以shell:buildAppEnginePackage任务为例。
其中的shell为任务名,而buildAppEnginePackage为shell任务下的一个目标名。

在文件中寻找一下,发现shell任务属性如下:

 shell: {
      getPythonTestDeps: {
        command: 'python build/get_python_test_deps.py'
      },
      installPythonTestDepsOnLinux: {
        command: 'python build/install_webtest_on_linux.py webtest-master/'
      },
      runPythonTests: {
        command: ['python', 'build/run_python_tests.py', 'google_appengine/',
                  out_app_engine_dir, 'webtest-master/'].join(' ')
      },
      buildAppEnginePackage: {
        command: ['python', './build/build_app_engine_package.py', 'src',
                  out_app_engine_dir].join(' ')
      },
      buildAppEnginePackageWithTests: {
        command: ['python', './build/build_app_engine_package.py', 'src',
                  out_app_engine_dir, '--include-tests'].join(' ')
      },
      removePythonTestsFromOutAppEngineDir: {
        command: ['python', './build/remove_python_tests.py',
                  out_app_engine_dir].join(' ')
      },
      genJsEnums: {
        command: ['python', './build/gen_js_enums.py', 'src',
                  'src/web_app/js'].join(' ')
      },
    },

从以上内容可以看到,buildAppEnginePackage的实质是执行

'python', './build/build_app_engine_package.py', 'src',
                  out_app_engine_dir

其中的out_app_engine_dir在文件开头定义,内容如下:

/* globals module */
var out_app_engine_dir = 'out/app_engine';

所以,buildAppEnginePackage应该是要执行

'python', './build/build_app_engine_package.py', 'src','out/app_engine'

更具体的含义就需要查看对应的python文件了。

类似的
‘shell:genJsEnums’,对应

'python', './build/gen_js_enums.py', 'src','src/web_app/js'

‘closurecompiler:debug’对应:

closurecompiler: {
      debug: {
        files: {               文件对象格式,一个目标可以多个src-dest对。
          // Destination: [source files]
          'out/app_engine/js/apprtc.debug.js': [       //其中out/app_engine/js/apprtc.debug.js为dest文件,属性名;[]为src文件
            'node_modules/webrtc-adapter/out/adapter.js',
            'src/web_app/js/analytics.js',
            'src/web_app/js/enums.js',
            'src/web_app/js/adapter.js',
            'src/web_app/js/appcontroller.js',
            'src/web_app/js/call.js',
            'src/web_app/js/constants.js',
            'src/web_app/js/infobox.js',
            'src/web_app/js/peerconnectionclient.js',
            'src/web_app/js/remotewebsocket.js',
            'src/web_app/js/roomselection.js',
            'src/web_app/js/sdputils.js',
            'src/web_app/js/signalingchannel.js',
            'src/web_app/js/stats.js',
            'src/web_app/js/storage.js',
            'src/web_app/js/util.js',
            'src/web_app/js/windowport.js',
          ]
        },
        options: {
          'compilation_level': 'WHITESPACE_ONLY',
          'language_in': 'ECMASCRIPT5',
          'formatting': 'PRETTY_PRINT'
        },
      },

分析一下:
closurecompiler:debug的会从如下一堆文件

'node_modules/webrtc-adapter/out/adapter.js',
            'src/web_app/js/analytics.js',
            'src/web_app/js/enums.js',
            'src/web_app/js/adapter.js',
            'src/web_app/js/appcontroller.js',
            'src/web_app/js/call.js',
            'src/web_app/js/constants.js',
            'src/web_app/js/infobox.js',
            'src/web_app/js/peerconnectionclient.js',
            'src/web_app/js/remotewebsocket.js',
            'src/web_app/js/roomselection.js',
            'src/web_app/js/sdputils.js',
            'src/web_app/js/signalingchannel.js',
            'src/web_app/js/stats.js',
            'src/web_app/js/storage.js',
            'src/web_app/js/util.js',
            'src/web_app/js/windowport.js',

生成out/app_engine/js/apprtc.debug.js文件
生成过程中,采用options中的属性,其中的具体含义没有找到确切的答案。

‘grunt-chrome-build’对应

'grunt-chrome-build' : {
      apprtc: {
        options: {
          buildDir: 'out/chrome_app',
          zipFile: 'out/chrome_app/apprtc.zip',
          // If values for chromeBinary and keyFile are not provided, the packaging
          // step will be skipped.
          // chromeBinary should be set to the Chrome executable on your system.
          chromeBinary: null,
          // keyFile should be set to the key you want to use to create the crx package
          keyFile: null,
          appwindowHtmlSrc: 'src/web_app/html/index_template.html',
          appwindowHtmlDest: 'out/chrome_app/appwindow.html'
        },
        files: [       //文件数组格式,多个src-dest文件映像,同时支持每个src-dest有各自的属性 files:[]为文件数组格式,files:{}为文件对象格式,区别在于是否允许不同的src-dest有不同的属性
          {
            expand: true,                      //处理大量的单一文件时,用来动态的构建文件列表。expand 为true时
            cwd: 'src/web_app/chrome_app',     //为之后src文件匹配时,都是相对此处的路径
            src: [
              'manifest.json'                  //应该src文件就是src/web_app/chrome_app/manifest.json
            ],
            dest: 'out/chrome_app/'            //目标文件路径前缀
          },
          {
          //expand属性为true时,后面的src/dest的路径都是相对cwd路径而言的
            expand: true, 
            cwd: out_app_engine_dir,
            src: [
            //通配符筛选文件,**可以表示文件路径,*表示任意数目字符,但不能代替‘/’
              '**/*.js',
              '**/*.css',
              '**/images/apprtc*.png',
              '!**/*.pem'
            ],
            dest: 'out/chrome_app/'
          },
          {
            expand: true,
            cwd: 'src/web_app',
            src: [                   //文件操作中的简洁模式,一个src-dest对。源文件为src值,目标文件为dest值,都是相对cwd值的路径,即src第一个文件为src/web_app/js/background.js
              'js/background.js',
              'js/appwindow.js',
              '!**/*.pem'
            ],
            dest: 'out/chrome_app/'
          }
        ],
      }
    },

具体执行应该是执行这4个任务。这4个任务的更具体的含义,之后有时间来分析一下。

本人也是边学习新知识边分析apprtc.欢迎指正!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章