一、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.歡迎指正!