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

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 3661|回复: 0

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

[复制链接]

1109

主题

1649

回帖

7万

积分

用户组: 管理员

一只技术宅

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

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

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

×
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脚本。
  1. AC_INIT([foo],[1.0])

  2. AC_LANG(C)
  3. AC_PROG_CC_C99

  4. AC_CHECK_HEADERS([stdio.h])
  5. AC_CHECK_HEADERS([fcntl.h])
  6. AC_CHECK_HEADERS([errno.h])
  7. AC_CHECK_HEADERS([ctype.h])
  8. AC_CHECK_HEADERS([signal.h])
  9. AC_CHECK_HEADERS([unistd.h])
  10. AC_CHECK_HEADERS([stdarg.h])
  11. AC_CHECK_HEADERS([malloc.h])
  12. AC_CHECK_HEADERS([memory.h])
  13. AC_CHECK_HEADERS([inttypes.h])
  14. AC_CHECK_HEADERS([sys/socket.h])
  15. AC_CHECK_HEADERS([arpa/inet.h])
  16. AC_CHECK_HEADERS([netinet/ip.h])

  17. AC_TYPE_SIZE_T

  18. AC_CONFIG_HEADERS(config.h)
  19. AC_CONFIG_FILES([makefile])
  20. 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开发者看)
  1. #ifndef _FOO_CONFIG_H_
  2. #define _FOO_CONFIG_H_

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

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

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

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

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

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

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

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

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

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

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

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

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

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

然后我们写makefile.in,用于让configure生成makefile。因为configure需要判断你用的编译器是啥,然后根据你用的编译器来生成对应的makefile脚本。
  1. CC=@CC@
  2. LD=@CC@
  3. CCFLAGS=@CCFLAGS@

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

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

写好这些以后,我们就可以写我们的主程序了。
  1. #include"config.h"

  2. #if HAVE_STDIO_H
  3. #include<stdio.h>
  4. #endif

  5. #if HAVE_FCNTL_H
  6. #include<fcntl.h>
  7. #endif

  8. #if HAVE_ERRNO_H
  9. #include<errno.h>
  10. #endif

  11. #if HAVE_CTYPE_H
  12. #include<ctype.h>
  13. #endif

  14. #if HAVE_SIGNAL_H
  15. #include<signal.h>
  16. #endif

  17. #if HAVE_UNISTD_H
  18. #include<unistd.h>
  19. #endif

  20. #if HAVE_STDARG_H
  21. #include<stdarg.h>
  22. #endif

  23. #if HAVE_MALLOC_H
  24. #include<malloc.h>
  25. #endif

  26. #if HAVE_MEMORY_H
  27. #include<memory.h>
  28. #endif

  29. #if HAVE_INTTYPES_H
  30. #include<inttypes.h>
  31. #endif

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

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

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

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

  3. #include"config.h"

  4. #if HAVE_STDIO_H
  5. #include<stdio.h>
  6. #endif

  7. #if HAVE_FCNTL_H
  8. #include<fcntl.h>
  9. #end if

  10. #if HAVE_ERRNO_H
  11. #include<errno.h>
  12. #endif

  13. #if HAVE_CTYPE_H
  14. #include<ctype.h>
  15. #endif

  16. #if HAVE_SIGNAL_H
  17. #include<signal.h>
  18. #endif

  19. #if HAVE_UNISTD_H
  20. #include<unistd.h>
  21. #endif

  22. #if HAVE_STDARG_H
  23. #include<stdarg.h>
  24. #endif

  25. #if HAVE_MALLOC_H
  26. #include<malloc.h>
  27. #endif

  28. #if HAVE_MEMORY_H
  29. #include<memory.h>
  30. #endif

  31. #if HAVE_INTTYPES_H
  32. #include<inttypes.h>
  33. #endif

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

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

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

  43. #endif
复制代码
然后我们在主程序源码里面包含它就好:
  1. #include"foocfg.h"

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

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

对于不看内容直接搓鼠标滚轮到底部的读者朋友们我这里贴心地提醒一句:
我这篇文章描述的是如何让自己的程序能在不同的平台下运行,编译的时候能自动判断所需的头文件是否存在,并且能自动找到合适的解决方案,得到所需的函数的声明和变量以及结构体的定义等。
知道了内容讲的是啥的话,再回到开始看应该就容易看懂了吧,抱歉我不是特别擅长写教程。

参考资料:
autoconf
https://www.gnu.org/software/autoconf/manual/autoconf.html

AC_PROG_CC等有关语言的检查
https://www.gnu.org/software/autoconf/manual/autoconf-2.60/html_node/C-Compiler.html

检查头文件的方法
https://www.gnu.org/software/autoconf/manual/autoconf-2.66/html_node/Configuration-Headers.html

回复

使用道具 举报

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-3-29 02:48 , Processed in 0.038556 second(s), 32 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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