C, C++, Obj-C

Android Studio中项目NDK原生部分整合方式

UPDATE 2016.11.14:
目前的新版Android Studio 2.2已经可以完美支持NDK原生代码整合,通过cmake以及lldb实现c/c++代码调试,并且支持代码补全等功能!

自从Android Studio出现以后,我就很少再使用Eclipse+ADT的组合做Android开发了,更快的反应速度和更人性化的操作方式以及智能感知方面等等的优势,确实体现出了Google最初宣传Android Studio时的大部分特点,不过这些种种优势目前还只是体现在Java应用开发部分,而对于需要用到C/C++的Android NDK开发时,目前还没有很好的支持,当然,参考目前Android Studio的版本迭代速度来看,这应该也只是时间问题,当初ADT刚出的时候也是问题多多,包括后来的CDT+NDK支持,最初也只是支持原生代码的编译生成so,过了很久很久的某次update才加入了原生代码的可视化调试功能(当时也是历尽万难才测试成功的,还只是很简单的混合ndk项目…)。

最近看到了ndk又更新到了r10b版本,而且又据说新版的Android Studio中已经加入了一些ndk支持,于是正好一次测试一下(Android Studio beta 0.8.14, android-ndk32-r10b-windows-x86_64):

Continue reading…

VS结合VisualGDB搭建OpenWrt交叉编译远程调试开发环境

由于此开发环境涉及3设备协同工作,因此先说明一下整体开发环境的配置以及每部分所负责的功能:
1.Windows PC
这里我用的是64位Win7,作为VS的安装环境Windows自然是必不可少的,本机主要用于结合VisualGDB插件完成项目创建,上传编译机编译并部署执行程序到测试机完成gdbserver远程调试(自然是背后gdb前脸VS的全图形可视化调试)
2.Linux PC
这个我这里是CentOS Linux release 7.2.1511 64bit,安装于VMWare虚拟机上(当然也可以用真机,效果更佳!),作为交叉编译OpenWrt执行程序的编译机,由VisualGDB通过ssh连接同步Win机上的项目源码,make文件等并进行编译,同时还作为gdb客户端连接OpenWrt调试服务端并由VisualGDB传递gdb调试操作间接实现Win端的VS图形化调试
3.OpenWrt Router
这个我这里使用的是联想的Y1S路由器,MT7620A mips架构,作为整个开发环境的目标平台,接收Linux编译后的执行程序,并负责启动gdbserver作为远程调试的服务端

整体环境介绍完了,首先要做的是搭建起Linux机上的OpenWrt命令行开发环境(Y1S对应的是OpenWrt-SDK-ramips-for-linux-x86_64-gcc-4.8-linaro_uClibc-0.9.33.2),并且保证可以使用原始gdb的方式成功进行命令行模式的远程调试,这个就不赘述了,网上有很多相关教程。补充一点,在gdb调试过程中可能会遇到(OpenWrt上线./gdbserver :9876 /mnt/sda1/kcamd启动调试进程):

./scripts/remote-gdb 192.168.99.1:9876 ./build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/kcam/kcamd

This GDB was configured as “–host=x86_64-linux-gnu –target=mipsel-openwrt-linux-uclibc”.

(gdb) b main
Breakpoint 1 at 0x400cac: file main.cpp, line 29.
(gdb) c
Continuing.
warning: `/usr/lib/libstdc++.so.6′: Shared library architecture unknown is not compatible with target architecture mips:isa32r2.
warning: `/lib/libgcc_s.so.1′: Shared library architecture unknown is not compatible with target architecture mips:isa32r2.
warning: Could not load shared library symbols for 5 libraries, e.g. /lib/libuci.so.
Use the “info sharedlibrary” command to see the complete listing.
Do you need “set solib-search-path” or “set sysroot”?

Breakpoint 1, (gdb) n
Remote ‘g’ packet reply is too long: 00000000f8ffffffecdee77740b4ff7701000000f46dff7ffc6dff7f2034847fec6eff7f000101819bffaaacf0ffffff00031c7fffffffff00000000c826e677da6eff7f1c094000406dff7f0098e477f8e0a0000e00000000004400dc7a430087040000900c40000000000000000000a013e877786aff7f786aff7f4c27e67713ff0001a070230029000000a0d1e17724008050ac0c4000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000
(gdb)

Continue reading…

使用distcc分布式编译加速Android NDK原生项目编译生成

随着项目规模的增大,源代码文件增多,结构越来越复杂,导致项目编译链接速度变慢是一件让人非常头痛的事!

在Windows上我们用Visual Studio可以使用IncrediBuild (http://www.incredibuild.com/) 这个非常好用的分布式编译工具,配合其自带的VS Add-In可以很方便的将大型项目的编译工作负担分布到网络上的其它机器完成,极大的缩短了项目编译时间,提高工作效率!

不过遗憾的是IncrediBuild目前只支持Windows系统和VS等一些编译环境,对于Android, iOS等交叉编译的移动平台开发环境就无能为力了。

其实对于linux系OS上还是有可用的分布式编译解决方案的,就是接下来我要说的这个distcc,项目介绍请猛击这里:https://code.google.com/p/distcc/

Continue reading…

Xcode升级后导致原Qt项目构建失败的问题

原有使用Qt Creator 4.0.2创建的跨平台GUI项目,在近日Xcode升级至8.0后,出现了构建失败的问题,错误信息如下:

clang: warning: no such sysroot directory: ‘/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk’
In file included from ../FbxVertexMerger/main.cpp:1:
In file included from ../FbxVertexMerger/mainwindow.h:4:
In file included from ../../../../Qt5.7.0/5.7/clang_64/lib/QtWidgets.framework/Headers/QMainWindow:1:
In file included from ../../../../Qt5.7.0/5.7/clang_64/lib/QtWidgets.framework/Headers/qmainwindow.h:43:
In file included from /Users/wzkres/Qt5.7.0/5.7/clang_64/lib/QtWidgets.framework/Headers/qwidget.h:43:
In file included from /Users/wzkres/Qt5.7.0/5.7/clang_64/lib/QtGui.framework/Headers/qwindowdefs.h:43:
In file included from /Users/wzkres/Qt5.7.0/5.7/clang_64/lib/QtCore.framework/Headers/qglobal.h:81:
/Users/wzkres/Qt5.7.0/5.7/clang_64/lib/QtCore.framework/Headers/qsystemdetection.h:95:12: fatal error: ‘TargetConditionals.h’ file not found
# include <TargetConditionals.h>

显然,问题出在MacOSX10.11.sdk这个位置,升级后的MacOSX SDK变为了MacOSX10.12.sdk,那么如何让Qt使用新版本的SDK呢?很简单,修改Qt项目的.pro文件,加入 Continue reading…

VC++项目中第三方库代码生成std::string对象的析构崩溃问题

在使用预编译好的第三方库时,一定要注意生成库时的Runtime Library运行时库是否与引用库项目所使用的一致,一般提供prebuilt库方都会按照这些规则给库文件命名,如mt、mt-d等。
这次在测试一个别人提供好的mongo库时,就遇到了由于运行时库不一致导致的莫名其妙崩溃问题,崩溃代码如下:

mongo::BSONObjBuilder builder;
builder.append("type", 2)
mongo::BSONObj obj = builder.obj();
obj.jsonString();

在jsonString()这一步,任何BSONObj的转std::string方法,如toString()等,构造出的std::string在析构时会导致崩溃,大致看了下是标准库中的析构函数内执行此处时触发的:

__CLR_OR_THIS_CALL ~basic_string()
    {   // destroy the string
    _Tidy(true);
    }

放开运行的话会提示堆已被破坏,修改了一些调用代码方式,无果,最后发现自行创建的std::string析构时执行同样逻辑没有问题,最终发现是由于dll库使用的是debug版运行时库,而主程序使用的是非debug运行时库所致,在VS中Configuration properties->C/C++->Code Generation->Runtime Library,选择一致的debug版运行时库后,问题解决。

CentOS 7下编译FBX SDK示例时的”cannot find -luuid”问题

在Linux环境下尝试编译FBX SDK中的samples时,最后链接时出现”cannot find -luuid”,查了下文档发现这个uuid库是linux下生成唯一id用的那么一个库,起初以为是少了什么lib或者devel之类的,于是用yum开始安装,结果libuuid、uuid-devel都装上了还是不行,于是又都逐一卸载掉,查看ld的search path,在/lib64/下找到了:

lrwxrwxrwx. 1 root root 16 4月 7 22:35 /lib64/libuuid.so.1 -> libuuid.so.1.3.0
-rwxr-xr-x. 1 root root 20032 4月 1 01:37 /lib64/libuuid.so.1.3.0

用yum确认了下,也确实是已安装有这个uuid相关的库以及include等,于是怀疑是符号链接的问题,试了下用

ln -s libuuid.so.1 libuuid.so

建立libuuid.so的符号链接,重新make,成功通过并生成了sample的执行文件。

PS: FBX SDK的sample中的MakeFile将CC和LD设置成了gcc4,这个在我的CentOS7下会找不到(默认安装的应该都是),于是直接删掉了4。

对于Android NDK编译器ARM和Thumb模式的理解及Toolchain的切换clang编译器

以前在做Symbian的时候,曾经研究过ARM CPU的指令集问题,ARM处理器支持两套指令集,即ARM和Thumb。ARM为32位指令集而Thumb为16位指令集,理论上32位可以提供更快的执行速度但会生成更大的二进制执行文件,而16位的Thumb则恰恰相反,省地儿但慢,这也正是体现出了ARM对于嵌入式设备的专业性。对于我这种牺牲一切换速度的理念来说,当时就留下了ARM就比Thumb快的印象,以致于现在在做Android NDK原生开发时,也是优先用ARM指令集。(这个可以通过在Android.mk中加入LOCAL_ARM_MODE := arm控制,默认情况下NDK使用Thumb指令集)

但是最近在Xcode编译iOS项目时,注意到同为ARM处理器的苹果设备,使用的是Thumb指令集,而且好像还是某种新版本的Thumb指令集,小搜索了一下看到有人说这种armv7引入的叫做Thumb-2的指令集要比arm指令集更好!于是又重新搜索更新了一下大脑知识库…

Continue reading…

OpenWRT下libv4l的编译问题(uClibc++、stdc++相关)

最近研究路由器上的摄像头监控,要用到libv4l2的库,结果用OpenWRT SDK编译时却遇到了问题,路由器cpu是MT7620A,用的SDK是OpenWrt-SDK-ramips-for-linux-x86_64-gcc-4.8-linaro_uClibc-0.9.33.2,用./scripts/feeds安装了libv4l后,直接make报错:

In file included from v4l2-compliance.cpp:37:0:
v4l2-compliance.h:25:18: fatal error: cerrno: No such file or directory
 #include <cerrno>

看了下,是c++写法下的errno头文件找不到,V=s看了详细日志,-I的头文件搜索路径里确实没有cerrno,但是这个文件确实存在于这里:staging_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/usr/include/uClibc++/,也就是说-I少了个带uClibc++的路径,看了半天sdk里的各种.mk,也没找到什么配置参数方面的玄机,可又不想傻加上个-I,或者都改成errno.h,于是开始搜索…
Continue reading…

iOS Android平台下的wchar_t默认size问题

很久以前曾经被wchar的问题在iOS和Android上被坑过,这次是在参研一份有年头的代码时又遇到了宽字符的问题,当然,也是和wchar有关系的,就是这个wchar在各个系统平台下的sizeof问题,关键的是一段UTF-8转UTF-16的代码:

size_t Utf8ToUtf16(const char* src_, wchar_t* dest_, size_t destlen_, size_t srclen_ /*= 0*/) 
{
	if (srclen_ == 0)
		srclen_ = _utf_length(src_);
	size_t destcapacity = destlen_;
	for (size_t idx = 0; ((idx < srclen_) && (destcapacity > 0));)
	{
		wchar_t	cp;
		unsigned char	cu = src_[idx++];
		if (cu < 0x80)
			cp = (wchar_t)(cu);
		else if (cu < 0xE0)
		{
			cp = ((cu & 0x1F) << 6);
			cp |= (src_[idx++] & 0x3F);
		}
		else if (cu < 0xF0)
		{
			cp = ((cu & 0x0F) << 12);
			cp |= ((src_[idx++] & 0x3F) << 6);
			cp |= (src_[idx++] & 0x3F);
		}
		else
		{
			cp = L'?';
		}
		*dest_++ = cp;
		--destcapacity;
	}
	return destlen_ - destcapacity;
}

这段代码在win下可以正常将utf-8的char*转换为utf-16的char*,但在Android, iOS下却不能得到和win下一样的转换结果,刚发现这个问题时,以为又是当年遇到的wchar坑,宽字符函数不支持之类的,但是仔细看了下并没有用到什么字符串函数,转换部分也是自己实现的,最后在各平台调试器的帮助下找到了问题:
Continue reading…

Cocos2d-X lua binding C++类的RTTI

在用Cococs2d-X的lua binding时,遇到了需要RTTI的时候,也就是运行时类型识别,这点对于lua类来说还是很容易做到的,可以参考引擎lua framework中cocos/cocos2d/functions.lua中的function iskindof(obj, classname),不过,这个函数的递归实现函数iskindof_有些小问题,这里是修正方法:

local iskindof_
iskindof_ = function(cls, name)
    local __index = rawget(cls, "__index")
    if type(__index) == "table" and rawget(__index, "__cname") == name then return true end
    
    if rawget(cls, "__cname") == name then return true end
    -- By K-Res: should use __index instead of cls!
--    local __supers = rawget(cls, "__supers")
    local __supers = rawget(__index, "__supers") 
    if not __supers then return false end
    for _, super in ipairs(__supers) do
        if iskindof_(super, name) then return true end
    end
    return false
end

然而,当遇到需要对binding到lua的native class进行RTTI就有些麻烦了,可以看到借助tolua过去的lua对象是usertable挂metatable实现的,而metatable中也没有可以用lua类(确切的说是模拟class行为的table)的RTTI方式,因为需要查找的一些__cname等meta成员并不存在,作为class标识的只有一个.isclass的bool值,看了下binding实现的原生代码,发现在不添加额外元表信息的前提下获取class名称是需要用到LUA_REGISTRYINDEX这个只能由原生代码获取到的lua全局信息注册表,于是放弃了通过lua代码实现绑定类类型识别的想法,转而使用原生函数实现了个简单的获取binding时设置的本类class名方法,暂时实现了本对象所属类识别的功能:
Continue reading…