最近用QTK开发一个下载(下载到开发板)工具,同时用到了Makefile/cmake和node-gyp,而且都要针对不同平台做不同的处理。这里做个记录,以备以后有需要时查阅。

Makefile

在Makefile中,可以用OS变量判断当前系统是否是Windows,然后用uname来判断当前系统是MacOS还是其它系统。

1
2
3
4
5
6
7
8
9
10
11
12
ifeq ($(OS),Windows_NT)
PLATFORM="Windows"
else
ifeq ($(shell uname),Darwin)
PLATFORM="MacOS"
else
PLATFORM="Unix-Like"
endif
endif
all:
@echo $(PLATFORM)

cmake

在cmake中,可以通过APPLE变量判断当前系统是否是MacOS,通过UNIX变量判断当前系统是否是UNIX,其它则认为是Windows。

1
2
3
4
5
6
7
if(APPLE)
//APPLE
elseif(UNIX)
//UNIX
else()
//Windows
endif()

node-gyp

在binding.gyp中,可以在conditions添加不同平台的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
'conditions': [
['OS=="mac"', {
'xcode_settings': {
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES'
},
"sources": ["native/serial/src/impl/list_ports/list_ports_osx.cc","native/serial/src/impl/unix.cc"]
},
'OS=="win"', {
"sources": ["native/serial/src/impl/list_ports/list_ports_win.cc","native/serial/src/impl/win.cc"],
'libraries': [
'-lsetupapi.lib',
'-lws2_32.lib'
]
}]
]

QTK中的Main Loop主要是管理QTK的渲染循环,与直接调用用requestAnimationFrame相比,它有下列好处:

1.多次请求重绘没有副作用。在QTK中每个控件的属性有变化时都需要重绘,控件并不需要知道别的控件有没有请求重绘,只管自己有变化时请求重绘就行了,这样势必出现重复请求重绘,MainLoop只在第一次请求重绘时才调用requestAnimationFrame,重复的请求只是增加一下计数。

2.把绘制分成绘制前,绘制和绘制后多个阶段。动画可以在绘制前这个阶段执行,这样参数的变化就能在本次绘制中体现出来,而一些不太重要的事情可以放到绘制后执行。

3.节省能源,绿色环保。控件有变化时请求重绘,没有变化时不需要重绘,只在有执行动画时才跑到最大帧率,而不是像游戏一样一直保持60FPS。

4.注册的回调函数会一直执行,直到注销为止。

使用方法如下(javascript):

1
2
3
4
5
6
function draw(evt) {
if(quit) {
qtk.MainLoop.off(qtk.MainLoop.DRAW, draw);
}
}
qtk.MainLoop.on(qtk.MainLoop.DRAW, draw);

QTK中的ImageTile表示一个图块,它可以是一整张图片,也可以是图片中的一块区域。x和y表示图块的位置,w和h表示图块的宽度和高度。

ImageTile的主要功能有两个:

加载指定URL的图片。

URL通常包括两个部分,用#分隔,前面部分是大图的信息,后面是图块的信息,如果没有#,则表示整张图片。目前支持4种格式的URL:

1.普通图片URL。此时ImageTile表示整张图片。如:

1
http://www.example.com/image.png

2.普通图片URL+’#’+x{x}y{y}w{w}h{h},它表示指定位置(xy)和大小(wh)的子图。如:

1
http://www.example.com/image.png#x10y10w100h100

3.普通图片URL+’#’+r{行数}c{列数}i{索引},它表示把图片分成指定行数和列数等大小的图块,取其中第i块,i的取值范围为0到r*c。如:

1
http://www.example.com/image.png#r3c3i0

4.JSON文件URL+’#’+小图的名称,它表示TexturePacker生成的JSON Hash格式文件中的一个小图。如:

1
http://www.example.com/images.json#0.png

将图块绘制在指定的HTML5 Canvas上。

ImageTile实现了多种绘制的方式,使用时指定绘制方式和目标区域的位置和大小,目前支持的绘制方式有:

1.缺省方式,将图块填满指定的区域。

2.居中方式,将图块按原大小绘制到指定区域的中间。

3.自动方式,将图块缩放显示到指定区域,保持图片的宽高不变。

4.特殊九宫格方式,将图块分成等大小的九块,按九宫格的方式显示。

5.水平三宫格方式,将图块按水平方向分成等大小的三块,左右两块分别显示在目标区域的左右两边,中间的一块缩放显示在目标区域的中间剩余部分。

6.垂直三宫格方式,将图块按垂直方向分成等大小的三块,上下两块分别显示在目标区域的上下两边,中间的一块缩放显示在目标区域的中间剩余部分。

7.水平平铺。水平方向平铺显示,垂直方向拉伸,如果想保证垂直方向不拉伸,请指定图块同样的高度。

8.垂直平铺。垂直方向平铺显示,水平方向拉伸,如果想保水平方向不拉伸,请指定图块同样的宽度。

9.平铺。水平和垂直方向均平铺显示。

QTK中的Assets主要是对APP使用的资源进行管理,资源包括图片、声音、字体和其它数据。

1.加载指定的资源。加载指定URL的资源,返回一个Promise。如果资源已经加载,就直接从缓存中获取,如果没有加载,就从网络获取,并放入到缓存中。

1
2
3
4
5
6
var asset = qtk.Assets.loadJSON("http://localhost:9876/base/www/not_exist.txt")
.then(json => {
}, err => {
});

2.预先加载资源。通常在APP启动时,预先加载某些资源。把这些资源放到一个分组中,分组中每加载完成一个资源,就会触发加载进度事件,界面可以根据加载进度事件更新进度条,整个分组加载完成后才启动APP。

1
2
3
4
5
6
7
8
9
10
11
12
13
var items = [
{type:qtk.Assets.TEXT, src:"http://localhost:9876/base/www/test.txt"},
{type:qtk.Assets.JSON, src:"http://localhost:9876/base/www/test.json"},
{type:qtk.Assets.IMAGE, src:"http://localhost:9876/base/www/test.jpg"},
{type:qtk.Assets.BLOB, src:"http://localhost:9876/base/www/test.blob"}
];
var assets = new qtk.Assets.Group(items);
assets.onProgress(function(info) {
if(info.total === info.loaded && info.loaded === items.length) {
done();
}
console.log(JSON.stringify(info));
});

3.清除缓存中的资源。一些资源在不需要时,可以从缓存中清除,以释放内存。

1
qtk.Assets.clear("http://localhost:9876/base/www/test.jpg");

我决定从开始就对QToolKit相关项目进行严格测试。由于对测试程序框架不熟悉,在选择测试程序框架时遇到一些问题。最初打算使用jest,倒不是看中它有强大自动mock功能,而是相信facebook自己使用的东西错不了。但是我在测试程序中使用XMLHTTPRequest时,总是提示网络不可用。在网上找到的答案都是关于如何mock XMLHTTPRequest的,而我更想进行实际的网络请求测试。所以只好重新审视自己的需求,希望找一个真正合适的测试程序框架:

1.能够在真实浏览器中测试。除了karma,好像没有什么可以选择。这货功能确实非常强大:

1
2
3
4
5
6
7
8
9
*.You want to test code in real browsers.
*.You want to test code in multiple browsers (desktop, mobile, tablets, etc.).
*.You want to execute your tests locally during development.
*.You want to execute your tests on a continuous integration server.
*.You want to execute your tests on every save.
*.You love your terminal.
*.You don't want your (testing) life to suck.
*.You want to use Istanbul to automagically generate coverage reports.
*.You want to use RequireJS for your source files.

2.使用起来方便,支持同步和异步功能的测试。karma不是测试程序框架也不是assert库,还需要自己选择相应的插件。我开始选择的是jasmine,用jasmine的人很多,上手也很简单。用了两天后,发现没法测试异步函数(后来找到了相应的插件),又去研究了人气很高的mocha,它对异步测试支持非常完美。果断选择了mocha, mocha也没有提供assert库,由使用者自己选择第三方assert库。我选择了better-assert,主要原因是它简单,而且合乎我的习惯。

3.能提供代码覆盖率的报表。karma也有converage相应的插件。使用起来比较方便。当然也有些小问题,一是源代码会被改得面目全非,如果有问题没法调试,所以调试版本需要关掉代码覆盖率的测试功能。二是第三方库也会被统计起来,而这些库通常没有专门的测试程序,这会拉低整体的覆盖率。为了让报表好看一点,也可以为这些库写一些测试程序。

4.方便集成github持续集成的功能。karma也支持第三方的持续集成系统,其中不少支持github,这一部分看起来比较简单,用到的时候再深入研究。

QToolKit是用Typescript开发的,先用tsc编译成JS,然后用webpack打包成一个文件,再使用上面的框架,整个过程还是比较流畅的:

1.先安装node及npm,为了避免不必要的麻烦,请安装最新的版本。

2.创建一个项目。

1
2
3
4
5
6
mkdir foo
cd foo
npm init
//test command输入下面的内容:
//tsc && webpack && ./node_modules/karma/bin/karma start

3.安装依赖的软件包,软件包比较多,建议使用淘宝的镜像

1
npm install karma karma-chrome-launcher karma-coverage karma-firefox-launcher karma-mocha mocha ts-loader typescript typings webpack --save

4.初始化Typescript项目,生成tsconfig.json。

1
tsc --init

5.编写代码。

src/sum.ts

1
2
3
export = function sum(x:number, y:number) : number {
return x + y;
}

src/index.ts

1
export import sum = require("./sum");

tests/sum.test.js

1
2
3
4
5
describe('sum', function() {
it('test sum', () => {
assert(foo.sum(100, 200) === 300);;
});
});

6.创建一个webpack的配置文件webpack.config.js。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var webpack = require('webpack');
var libraryName = 'foo';
module.exports = {
cache: true,
entry: {
index : './src/index.js'
},
output: {
path: '',
filename: '[name].js',
library: libraryName
},
resolve: {
extensions: ['', '.webpack.js', '.web.js', '.js']
}
}

7.创建karma的配置文件,先运行下面的命令,再手工修改:

1
./node_modules/karma/bin/karma init

加入测试需要的JS文件:

1
2
3
4
5
files: [
'index.js',
'__tests__/assert.js',
'__tests__/*.test.js'
],

增加覆盖率支持:

1
2
3
4
5
6
7
8
9
10
preprocessors: {
'index.js': ['coverage']
},
reporters: ['progress', 'coverage'],
overageReporter: {
type : 'html',
dir : 'coverage/'
},

基本功能完成了,运行测试:

1
npm run test

这时会自动启动浏览器进行测试,浏览器中有个Debug按钮,点击Debug按钮可以打开被测试的页面,如果有问题可以在浏览器中调试。覆盖率的数据在coverage目录下,这个是在karma的配置文件中指定的。

里面的配置参数很多,等用到了再去研究。

参考:Foo

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

QTK 是一套基于HTML5 Canvas实现的应用程序框架,和其它HTML5的框架相比,它并不适合开发网页,而是专注于应用程序开发。

QTK主要特色如下:

1.基于HTML5 Canvas实现。可以实现任何Native风格的控件,HTML5 Canvas使用硬件加速,轻松实现60FPS的动画。

2.高效率,低功耗。JS代码针对V8优化,执行效率高。在界面有变化时才重新绘制,支持脏矩形,只绘制变化的部分,让计算开销降到最低。

3.用QTK本身开发的IDE。支持在线版本和本地版本,让开发变得更简单。

4.即可以开发移动应用程序,又可以开发桌面应用程序。QTK内部抽象出两者的不同,在运行时自动安装相应的策略。

5.与传统HTML5控件良好互通。可以在QTK中使用HTML5元素,也可以在HTML5的控件中使用QTK。我们还计划支持React和WebComponent的编程方式。

6.完整的测试程序。QTK使用Karma + Mocha对所以组件进行测试,通过不断完善测试程序,让你没有后顾之忧。

7.完整的文档和示例。确保已经实现的控件,对使用者都是友好的,你可以无障碍的使用它们,请告诉我们任何让你产生挫折的地方,一定会得到优先解决。在开发的过程中我们也会在博客中写出QTK内部实现原理,以及做出某些决策的原因。

说来惭愧,最近才知道的whatwg-fetch的这个标准,firefox和chrome已经支持它了,github实现了相应的polyfill,typings里也有相应的定义,所以决定在qtk里直接使用,而不是再去包装XMLHttpRequest。

安装JS包

1
2
cnpm install es6-promise --save
cnpm install whatwg-fetch --save

安装typings

1
2
typings install dt~es6-promise --save --global
typings install dt~whatwg-fetch --save --global

引入相应的包

1
2
import Promise = require("es6-promise");
import fetch = require("whatwg-fetch");

却奇怪的是出现错误:

1
error TS2307: Cannot find module 'whatwg-fetch'.

查了一些资料才知道,这种polyfill,只是修改了一些全局的状态,并没有导出什么东西的包,应该用下面的方式导入:

1
import 'whatwg-fetch';

还是typescript不熟悉啊,得花点时间系统的学习一下。

参考:https://www.typescriptlang.org/docs/handbook/modules.html