技术宅的结界

 找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 690|回复: 0
收起左侧

【C】【面向Windows开发者】了解Unix跨平台编程:用autoconf判断你需要的头文件和函数在当前平台是否可用!

[复制链接]

986

主题

2166

帖子

5万

积分

用户组: 管理员

一只技术宅

UID
1
精华
197
威望
261 点
宅币
15947 个
贡献
30926 次
宅之契约
0 份
在线时间
1518 小时
注册时间
2014-1-26
发表于 2017-1-15 22:50:37 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有帐号?立即注册→加入我们

x
Windows下用得最多的开发环境大概是(或者说肯定是)VS了,VS虽然不怎么遵循C语言规范,但这个具有智能提示、自动补全、代码上色功能(作为对比的是记事本、edit.com、vi等),内置了调试器,并且具有可视化的工程配置编辑器(作为对比的是各种makefile)的开发工具还是相当受欢迎的,无论是新手还是老司机都能用得很爽(除了那些连安装都不会的新手),因此诞生了一批又一批不熟悉跨平台编程的Windows大牛。

跨平台编程,其实严格意义上来说是跨Unix系的跨平台编程,批命令用的是bash而非batch(前一个发音是“拜史”而另一个是“拜尺”),autoconf会根据你需要用到的头文件来帮你生成一个configure脚本(并且这个脚本是bash的,而不是batch的)。运行这个脚本后,它会判断你需要的函数和头文件在不在,然后给你生成一个头文件,config.h(但你需要先写一个config.h.in)以及其它的文件比如makefile(同样根据你的makefile.in来判断)

实际说一下autoconf的操作吧。假定我们的程序名字叫“foo”,并且它会用到以下几个头文件:

stdio.h
fcntl.h
errno.h
ctype.h
signal.h
unistd.h
stdarg.h
malloc.h
memory.h
inttypes.h
sys/socket.h
arpa/inet.h
netinet/ip.h

首先我们需要写configure.ac,当我们写好以后,autoconf就会自动从当前文件夹读取configure.ac,然后根据它的内容给你生成configure脚本。
[Plain Text] 纯文本查看 复制代码
AC_INIT([foo],[1.0])

AC_LANG(C)
AC_PROG_CC_C99

AC_CHECK_HEADERS([stdio.h])
AC_CHECK_HEADERS([fcntl.h])
AC_CHECK_HEADERS([errno.h])
AC_CHECK_HEADERS([ctype.h])
AC_CHECK_HEADERS([signal.h])
AC_CHECK_HEADERS([unistd.h])
AC_CHECK_HEADERS([stdarg.h])
AC_CHECK_HEADERS([malloc.h])
AC_CHECK_HEADERS([memory.h])
AC_CHECK_HEADERS([inttypes.h])
AC_CHECK_HEADERS([sys/socket.h])
AC_CHECK_HEADERS([arpa/inet.h])
AC_CHECK_HEADERS([netinet/ip.h])

AC_TYPE_SIZE_T

AC_CONFIG_HEADERS(config.h)
AC_CONFIG_FILES([makefile])
AC_OUTPUT
例子中的第一行表示工程的名字叫“foo”,然后“AC_LANG(C)”指定编程语言为C,使用C编译器,“AC_PROG_CC_C99”表示要求必须要有遵循C99规范的C编译器,其实也可以换成“AC_PROG_CC”,也就是只要是C编译器就行。对于C++,用“AC_PROG_CPP”,参考资料见文末
后面的AC_CHECK_HEADERS是判断指定的头文件是否存在,是的话,会把判断结果写入到config.h里面。
AC_TYPE_SIZE_T则是判断size_t是否存在,如果不存在,它就会自动根据需求给你定义size_t的类型。
最后面的AC_CONFIG_HEADERS是指定config.h这个文件的名字,你可以改成“cfg.h”,这样configure脚本就会自动把判断结果写入到cfg.h里面,而不是之前说的config.h了,此外对应的,你也要写cfg.h.in,而不是config.h.in。

然后autoconf会根据config.h.in的内容生成config.h,以及根据makefile.in生成makefile。

config.h.in的写法是:把你需要的头文件的文件名转换为大写,把句点和斜杠换成下划线“_”,然后前面加个“#undef ”,其它地方则自己补全你需要的东西就行。
按照本文的例子,你应该像这样写:(虽说注释什么的可以不要,但最好写上,给Windows开发者看)
[C] 纯文本查看 复制代码
#ifndef _FOO_CONFIG_H_
#define _FOO_CONFIG_H_

/* Define as 1 if you have stdio.h. */
#undef HAVE_STDIO_H

/* Define as 1 if you have fcntl.h. */
#undef HAVE_FCNTL_H

/* Define as 1 if you have errno.h. */
#undef HAVE_ERRNO_H

/* Define as 1 if you have ctype.h. */
#undef HAVE_CTYPE_H

/* Define as 1 if you have signal.h. */
#undef HAVE_SIGNAL_H

/* Define as 1 if you have unistd.h. */
#undef HAVE_UNISTD_H

/* Define as 1 if you have stdarg.h. */
#undef HAVE_STDARG_H

/* Define as 1 if you have malloc.h. */
#undef HAVE_MALLOC_H

/* Define as 1 if you have memory.h. */
#undef HAVE_MEMORY_H

/* Define as 1 if you have inttypes.h. */
#undef HAVE_INTTYPES_H

/* Define as 1 if you have sys/socket.h. */
#undef HAVE_SYS_SOCKET_H

/* Define as 1 if you have arpa/inet.h. */
#undef HAVE_ARPA_INET_H

/* Define as 1 if you have netinet/ip.h. */
#undef HAVE_NETINET_IP_H

#endif
这样的话,configure会根据它判断的结果,来帮你定义指定的宏,比如它找到了stdio.h,那么它给你生成的config.h里面,就会有#define HAVE_STDIO_H 1

然后我们写makefile.in,用于让configure生成makefile。因为configure需要判断你用的编译器是啥,然后根据你用的编译器来生成对应的makefile脚本。
[Plain Text] 纯文本查看 复制代码
CC=@CC@
LD=@CC@
CCFLAGS=@CCFLAGS@

foo: foo.o
	$(LD) -o $@ $^

.PHONY: clean
clean:
	rm -f foo *.o
其中的CC=@CC@是指定C编译器为configure找到的C编译器,LD是链接器,C编译器也带链接功能。CCFLAGS是C编译器使用的命令行参数,configure会自动帮你配置。

写好这些以后,我们就可以写我们的主程序了。
[C] 纯文本查看 复制代码
#include"config.h"

#if HAVE_STDIO_H
#include<stdio.h>
#endif

#if HAVE_FCNTL_H
#include<fcntl.h>
#endif

#if HAVE_ERRNO_H
#include<errno.h>
#endif

#if HAVE_CTYPE_H
#include<ctype.h>
#endif

#if HAVE_SIGNAL_H
#include<signal.h>
#endif

#if HAVE_UNISTD_H
#include<unistd.h>
#endif

#if HAVE_STDARG_H
#include<stdarg.h>
#endif

#if HAVE_MALLOC_H
#include<malloc.h>
#endif

#if HAVE_MEMORY_H
#include<memory.h>
#endif

#if HAVE_INTTYPES_H
#include<inttypes.h>
#endif

#if HAVE_SYS_SOCKET_H
#include<sys/socket.h>
#endif

#if HAVE_ARPA_INET_H
#include<arpa/inet.h>
#endif

#if HAVE_NETINET_IP_H
#include<netinet/ip.h>
#endif

int main()
{
	printf("Hello World!\n");
	return 0;
}
但我们在主程序写这么多#if感觉非常变态,这会让代码变得很难读,重要的内容都在后面了,前面占去了太多行。因此常见的是写一个自己的config配置头文件,这边我就写foocfg.h,像下面这样:
[C] 纯文本查看 复制代码
#ifndef _FOOCFG_H_
#define _FOOCFG_H_

#include"config.h"

#if HAVE_STDIO_H
#include<stdio.h>
#endif

#if HAVE_FCNTL_H
#include<fcntl.h>
#end if

#if HAVE_ERRNO_H
#include<errno.h>
#endif

#if HAVE_CTYPE_H
#include<ctype.h>
#endif

#if HAVE_SIGNAL_H
#include<signal.h>
#endif

#if HAVE_UNISTD_H
#include<unistd.h>
#endif

#if HAVE_STDARG_H
#include<stdarg.h>
#endif

#if HAVE_MALLOC_H
#include<malloc.h>
#endif

#if HAVE_MEMORY_H
#include<memory.h>
#endif

#if HAVE_INTTYPES_H
#include<inttypes.h>
#endif

#if HAVE_SYS_SOCKET_H
#include<sys/socket.h>
#endif

#if HAVE_ARPA_INET_H
#include<arpa/inet.h>
#endif

#if HAVE_NETINET_IP_H
#include<netinet/ip.h>
#endif

#endif
然后我们在主程序源码里面包含它就好:
[C] 纯文本查看 复制代码
#include"foocfg.h"

int main()
{
	printf("Hello World!\n");
	return 0;
}
这样的话,我们这个示例程序就配置好了。
接下来运行autoconf,然后运行configure,最后运行make,就好了。
20170115234632.png

示例源码附件下载:
foo.tar.gz (948 Bytes, 下载次数: 1)

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|技术宅的结界 ( 滇ICP备16008837号|网站地图

GMT+8, 2018-5-27 05:24 , Processed in 0.085165 second(s), 16 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表