用MinGW编译AWTK填坑录

AWTK 全称为 Toolkit AnyWhere,是 ZLG 倾心打造的一套基于 C 语言开发的 GUI 框架。旨在为用户提供一个功能强大、高效可靠、简单易用、可轻松做出炫酷效果的 GUI 引擎,并支持跨平台同步开发,一次编程,终生使用。

主要特色:

  • 开源免费,使用无拘束;
  • 支持纯 C 语言编程,使用无门槛;
  • 小巧高效,最小仅需 8K RAM+32K FLASH,可运行在 Cortex-M3 等小资源平台;
  • 拥有完善的动画系统,半透明填充和贴图,支持硬件加速,轻松做出炫酷流畅的界面效果;
  • 丰富的 GUI 控件,提供窗口、对话框和各种常用的控件,并内置多种动画效果;
  • 支持位图字体和矢量字体,可加载标准的 TTF 字体文件;
  • 内置中英文输入法,并支持智能词组输入;
  • 支持 XML 进行界面布局,主题和样式表技术,轻松实现界面换肤;
  • 支持视网膜高清分辨率技术,配合高清屏可呈现平滑细腻的图形和文字。

上面简单介绍了一下ZLG的GUI,下面开始填坑。

填坑之前先说一下为啥给自己挖坑。在windows下搞C语言设计开发,可能更多人会选择VS,但是编译AWTK需要比较高版本的VS,这对我来说体验不是很好,VS的安装包体积实在是太大了,启动又慢,我只需要用到编译代码这一个基础功能,显然VS不是理想的选择。

后来在AWTK的帮助文档里看到,AWTK可以使用MinGW作为编译工具,而且官方提供了使用方法,遂决定试用一下。实践结果告诉我,官方的使用方法根本编译不过去,这才有了填坑记录。

系统:WIN7 X64 旗舰版

硬件:i5 M560+3G内存+64G固态

软件:AWTK-Designer-x64-0.1.3-Preview-Setup      python-3.8.3-amd64     awtk-scons-mingw

1、ZLG官方提供了一个GUI界面设计编辑器,需要注册使用,下载地址:https://awtk.zlg.cn/

点击 登录 按钮下方的 客户端下载 即可。下载得到的是一个64位的安装程序,只能在64位系统下使用,下面介绍安装过程。

双击AWTK-Designer安装包进行安装,默认安装到C盘。

AWTK-Designer安装完成后,可以在SDK文件夹下找到AWTK的全部源码,无需另外下载,但有可能不是最新的版本,如果需要最新的源码,需要到主源码仓库:https://github.com/zlgopen/awtk  进行更新。

2、从python官网下载64位的安装包,地址:https://www.python.org/downloads/windows/

双击安装包,在 Add Python 3.8 to PATH 前打勾,然后选择自定义安装。

修改安装路径

其他选项保持默认。

下载scons,地址:https://scons.org/pages/download.html

解压到任意目录,打开CMD,进入scons目录,输入

python setup.py install

进行安装。

3、AWTK提供了MinGW,地址:https://github.com/zlgopen/awtk-scons-mingw

将MinGW文件夹解压到C盘,然后添加环境变量。

CMD窗口输入

gcc -v

可以看到,ZLG提供的MinGW是32位的。

4、目前看,工具是备齐了,编译试试看,有不足的地方再补充。

先来看看awtk-scons-mingw的官方说明

# awtk-scons-mingw
build awtk with scons and mingw  on windows

* 将 awtk 和本项目取到同一级目录

  ```
  cd c:\zlgopen
  git clone https://github.com/zlgopen/awtk.git
  git clone https://github.com/zlgopen/awtk-scons-mingw.git
  ```

* 安装 scons,并设置好 MinGW 的 Path 环境变量

  ```
  C:\zlgopen\awtk-scons-mingw\MinGW\bin
  ```

  ![mingw_path](docs/images/mingw_path.png)

* 修改 awtk 的编译配置 awtk_config.py,使用 mingw 作为编译工具

  ```
  NATIVE_WINDOW='sdl'
  TOOLS_NAME = ''
  TOOLS_NAME = 'mingw'  # 把该行前面的注释去掉
  ```

* 进入目录 awtk,执行 scons 编译

  ```
  cd awtk
  scons
  ```

* 编译其他的 Demo

  ```
  cd c:\zlgopen
  git clone https://github.com/zlgopen/awtk-examples.git
  cd awtk-examples\Chart-Demo
  scons
  ```

我们按照官方指示,用记事本打开C:\AWTK\SDK\awtk\awtk_config.py,找到

NATIVE_WINDOW='sdl'
TOOLS_NAME = ''
# TOOLS_NAME = 'mingw'

修改为

NATIVE_WINDOW='sdl'
TOOLS_NAME = ''
TOOLS_NAME = 'mingw'

在C:\AWTK\SDK\awtk目录下打开CMD,输入scons,执行编译。随着CPU风扇一阵狂转,命令停在了下面这个位置

gcc -o src\platforms\pc\fs_os.o -c -DWINDOWS -D_CONSOLE -g -Wall -DTK_ROOT=\"C:\
\AWTK\\SDK\\awtk\" -DWITH_ASSET_LOADER -DWITH_FS_RES -DWITH_ASSET_LOADER_ZIP -DS
TBTT_STATIC -DSTB_IMAGE_STATIC -DWITH_STB_IMAGE -DWITH_SOCKET -DWITH_VGCANVAS -D
WITH_UNICODE_BREAK -DWITH_DESKTOP_STYLE -DSDL2 -DHAS_STD_MALLOC -DWITH_SDL -DHAS
_STDIO -DHAVE_STDIO_H -DWITH_STB_FONT -DWITH_BITMAP_BGRA -DWITH_NANOVG_SOFT -DWI
TH_FB_BGR565=1 -DWITH_NANOVG_AGGE -DWITH_DOUBLE_FLOAT -DUNICODE -DSDL_REAL_API -
DSDL_HAPTIC_DISABLED -DSDL_SENSOR_DISABLED -DSDL_JOYSTICK_DISABLED -D__STDC_LIMI
T_MACROS -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS -D_HAS_EXCEPTIONS=0 -D_
HAS_ITERATOR_DEBUGGING=0 -D_ITERATOR_DEBUG_LEVEL=0 -D_SCL_SECURE=0-D_SECURE_SCL=
0 -D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE
 -I. -Isrc -I3rd -Isrc\ext_widgets -I3rd\pixman -I3rd\cairo -I3rd\bgfx\bgfx\incl
ude -I3rd\bgfx\bx\include -I3rd\bgfx\bimg\include -I3rd\agge -I3rd\agg\include -
I3rd\nanovg -I3rd\nanovg\gl -I3rd\nanovg\base -I3rd\nanovg\agge -I3rd\nanovg\bgf
x -I3rd\SDL\src -I3rd\SDL\include -I3rd\agge\src -I3rd\agge\include -I3rd\gpinyi
n\include -I3rd\libunibreak -I3rd\gtest\googletest -I3rd\gtest\googletest\includ
e -Itools src\platforms\pc\fs_os.c
=====
b"src\\platforms\\pc\\fs_os.c: In function 'fs_os_file_exist':\nsrc\\platforms\\
pc\\fs_os.c:189:21: error: storage size of 'st' isn't known\n   struct _stat64i3
2 st;\n                     ^~\nsrc\\platforms\\pc\\fs_os.c:189:21: warning: unu
sed variable 'st' [-Wunused-variable]\nsrc\\platforms\\pc\\fs_os.c: In function
'fs_os_dir_exist':\nsrc\\platforms\\pc\\fs_os.c:296:21: error: storage size of '
st' isn't known\n   struct _stat64i32 st;\n                     ^~\nsrc\\platfor
ms\\pc\\fs_os.c:296:21: warning: unused variable 'st' [-Wunused-variable]\nsrc\\
platforms\\pc\\fs_os.c: In function 'fs_os_get_file_size':\nsrc\\platforms\\pc\\
fs_os.c:327:21: error: storage size of 'st' isn't known\n   struct _stat64i32 st
;\n                     ^~\nsrc\\platforms\\pc\\fs_os.c:327:21: warning: unused
variable 'st' [-Wunused-variable]\nsrc\\platforms\\pc\\fs_os.c: In function 'fs_
os_get_cwd':\nsrc\\platforms\\pc\\fs_os.c:390:33: warning: passing argument 2 of
 'GetCurrentDirectoryW' from incompatible pointer type [-Wincompatible-pointer-t
ypes]\n   GetCurrentDirectory(MAX_PATH, path);\n
 ^~~~\nIn file included from c:\\mingw\\include\\windows.h:44,\n
 from src\\platforms\\pc\\fs_os.c:6:\nc:\\mingw\\include\\winbase.h:1669:54: not
e: expected 'LPWSTR' {aka 'short unsigned int *'} but argument is of type 'char
*'\n WINBASEAPI DWORD WINAPI GetCurrentDirectoryW (DWORD, LPWSTR);\n
                                          ^~~~~~\nsrc\\platforms\\pc\\fs_os.c: I
n function 'fs_os_stat':\nsrc\\platforms\\pc\\fs_os.c:405:21: error: storage siz
e of 'st' isn't known\n   struct _stat64i32 st;\n                     ^~\nsrc\\p
latforms\\pc\\fs_os.c:405:21: warning: unused variable 'st' [-Wunused-variable]\
nsrc\\platforms\\pc\\fs_os.c: In function 'fs_os_get_file_size':\nsrc\\platforms
\\pc\\fs_os.c:348:1: warning: control reaches end of non-void function [-Wreturn
-type]\n }\n ^\n"
=====
scons: *** [src\platforms\pc\fs_os.o] Error 1
scons: building terminated because of errors.

咱们把报错的这几句整理一下,方便查看

=====
b"src\\platforms\\pc\\fs_os.c: In function 'fs_os_file_exist':
src\\platforms\\pc\\fs_os.c:189:21: error: storage size of 'st' isn't known
   struct _stat64i32 st;
                     ^~
src\\platforms\\pc\\fs_os.c:189:21: warning: unused variable 'st' [-Wunused-variable]
src\\platforms\\pc\\fs_os.c: In function'fs_os_dir_exist':
src\\platforms\\pc\\fs_os.c:296:21: error: storage size of 'st' isn't known
   struct _stat64i32 st;
                     ^~
src\\platforms\\pc\\fs_os.c:296:21: warning: unused variable 'st' [-Wunused-variable]
src\\platforms\\pc\\fs_os.c: In function 'fs_os_get_file_size':
src\\platforms\\pc\\fs_os.c:327:21: error: storage size of 'st' isn't known
   struct _stat64i32 st;
                     ^~
src\\platforms\\pc\\fs_os.c:327:21: warning: unusedvariable 'st' [-Wunused-variable]
src\\platforms\\pc\\fs_os.c: In function 'fs_os_get_cwd':
src\\platforms\\pc\\fs_os.c:390:33: warning: passing argument 2 of 'GetCurrentDirectoryW' from incompatible pointer type [-Wincompatible-pointer-types]
   GetCurrentDirectory(MAX_PATH, path);
 ^~~~
In file included from c:\\mingw\\include\\windows.h:44,
 from src\\platforms\\pc\\fs_os.c:6:
c:\\mingw\\include\\winbase.h:1669:54: note: expected 'LPWSTR' {aka 'short unsigned int *'} but argument is of type 'char*'
 WINBASEAPI DWORD WINAPI GetCurrentDirectoryW (DWORD, LPWSTR);
                                          ^~~~~~
src\\platforms\\pc\\fs_os.c: In function 'fs_os_stat':
src\\platforms\\pc\\fs_os.c:405:21: error: storage size of 'st' isn't known
   struct _stat64i32 st;
                     ^~
src\\platforms\\pc\\fs_os.c:405:21: warning: unused variable 'st' [-Wunused-variable]
src\\platforms\\pc\\fs_os.c: In function 'fs_os_get_file_size':
src\\platforms\\pc\\fs_os.c:348:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^
"
=====
scons: *** [src\platforms\pc\fs_os.o] Error 1
scons: building terminated because of errors.

很明显,在C:\AWTK\SDK\awtk\src\platforms\pc\fs_os.c的第189、296、327、405行出现了

error: storage size of 'st' isn't known

这样的错误。看来编译器不能准确识别st这个变量,我们来看看st是如何定义的。

struct _stat64i32 st;

这四处的定义都一样。struct好理解,那_stat64i32是啥?经过查找,_stat64i32的定义在C:\MinGW\include\sys\stat.h中

/* The structure manipulated and returned by stat() and fstat(); note that
 * expansion of the macro provided below will yield variants of struct stat
 * to conform with Microsoft's usage, (and POSIX usage up to and including
 * POSIX.1-2001, but NOT the extended specification of POSIX.1-2008).
 *
 * NOTE: If called on a directory the values in the time fields are not only
 * invalid, they will cause localtime et. al. to return NULL. And calling
 * asctime with a NULL pointer causes an Invalid Page Fault. So watch it!
 */
#define __struct_stat_defined(__st_off_t, __st_time_t)			     \
{ _dev_t	st_dev; 	/* Equivalent to drive number 0=A 1=B ... */ \
  _ino_t	st_ino; 	/* Always zero ? */			     \
  _mode_t	st_mode;	/* See above constants */		     \
   short 	st_nlink;	/* Number of links. */			     \
   short 	st_uid; 	/* User: Maybe significant on NT ? */	     \
   short 	st_gid; 	/* Group: Ditto */			     \
  _dev_t	st_rdev;	/* Seems useless (not even filled in) */     \
  __st_off_t	st_size;	/* File size in bytes */		     \
  __st_time_t	st_atime;	/* Access time (always 00:00 on FAT) */	     \
  __st_time_t	st_mtime;	/* Modified time */			     \
  __st_time_t	st_ctime;	/* Creation time */			     \
}

#if defined __MSVCRT__
/* This variant of struct stat is required to support the use of the
 * _stati64() function, which is provided by MSVCRT.DLL, but was not
 * present in CRTDLL.DLL...
 */
struct _stati64 __struct_stat_defined( __off64_t, time_t );

#if __MSVCRT_VERSION__ >= __MSVCR61_DLL || _WIN32_WINNT >= _WIN32_WINNT_WIN2K
/* ...while this supports the use of the _stat64() function, introduced
 * by MSVCR61.DLL, and subsequently added to MSVCRT.DLL for releases from
 * Win2K onwards...
 */
struct __stat64 __struct_stat_defined( __off64_t, __time64_t );

#if __MSVCRT_VERSION__ >= __MSVCR80_DLL
/* ...and these are specific to additional function variants, added to
 * the non-free MSVCR80.DLL, and its later derivatives, but not present
 * in MSVCRT.DLL (or CRTDLL.DLL).
 */
struct __stat32 __struct_stat_defined( __off32_t, __time32_t );
struct _stat32i64 __struct_stat_defined( __off64_t, __time32_t );
struct _stat64i32 __struct_stat_defined( __off32_t, __time64_t );

#endif	/* __MSVCRT_VERSION__ >= __MSVCR80_DLL */
#endif	/* __MSVCRT_VERSION__ >= __MSVCR61_DLL */
#endif	/* __MSVCRT__ */

也就是说,struct _stat64i32 __struct_stat_defined( __off32_t, __time64_t );被声明的条件必须是__MSVCRT_VERSION__ >= __MSVCR80_DLL,那么__MSVCRT_VERSION__ 是多少呢?

查看C:\MinGW\include\msvcrtver.h

/* When it is intended to link an application with any one of the
 * MSVC version specific MSVCRxx.DLL libraries, rather than with the
 * OS default MSVCRT.DLL, the particular substitute MSVCRxx.DLL may
 * be specified as any one of the following...
 */
#define __MSVCR60_DLL		0x0600
#define __MSVCR61_DLL		0x0601
#define __MSVCR70_DLL		0x0700
#define __MSVCR71_DLL		0x0701
#define __MSVCR80_DLL		0x0800
#define __MSVCR90_DLL		0x0900
#define __MSVCR100_DLL		0x1000
#define __MSVCR110_DLL		0x1100
#define __MSVCR120_DLL		0x1200

#ifndef __MSVCRT_VERSION__
/* This may be set, when the intent is to link with any of the above
 * non-freely distributable MSVCRxx.DLL libraries, rather than with the
 * pseudo-free MSVCRT.DLL provided as an OS component.  High byte is the
 * major version number, low byte is the minor; however, users are advised
 * to use custom GCC specs files to set this, while also substituting the
 * appropriate library in place of MSVCRT.DLL, rather than to simply set
 * it directly.
 *
 * It should be noted that __MSVCRT_VERSION__ is NOT a good indicator of
 * evolving MSVCRT.DLL features; that is better accomplished by using the
 * NTDDI_VERSION setting from the Windows API.  Thus, users of MSVCRT.DLL
 * should NOT set __MSVCRT_VERSION__, leaving us to establish a default,
 * equivalent to MSVCR60.DLL, which seems reasonably well aligned with
 * the feature set of the earliest MSVCRT.DLL version we support.
 */
# define __MSVCRT_VERSION__  __MSVCR60_DLL
#endif

可知__MSVCRT_VERSION__ 的默认值是__MSVCR60_DLL,说明MinGW 默认链接的是 msvcrt.dll,显然不满足__MSVCRT_VERSION__ >= __MSVCR80_DLL的要求。那么,我的windows系统里到底有哪些版本的MSVCRxx.DLL呢?

由此可见,我的C:\Windows\System32下只有MSVCR100.DLL、MSVCR110.DLL、MSVCR120.DLL这三个。当然,msvcrt.dll是肯定有的。

解决这个问题有两个思路,一是自行定义__MSVCRT_VERSION__的值,让它大于等于 __MSVCR80_DLL,比如我这里可以令__MSVCRT_VERSION__=__MSVCR100_DLL,用记事本打开C:\AWTK\SDK\awtk\awtk_config.py,找到

  elif TOOLS_NAME == 'mingw' :
    OS_LIBS=['kernel32', 'gdi32', 'user32', 'winmm','imm32','version','shell32','ole32','Oleaut32','Advapi32','oleaut32','uuid','stdc++']
    OS_FLAGS='-DWINDOWS -D_CONSOLE -g -Wall'
    COMMON_CFLAGS=COMMON_CFLAGS+' -std=gnu99 '
    COMMON_CCFLAGS=COMMON_CCFLAGS+' -DWITH_DOUBLE_FLOAT -DUNICODE ' 

修改为

  elif TOOLS_NAME == 'mingw' :
    OS_LIBS=['kernel32', 'gdi32', 'user32', 'winmm','imm32','version','shell32','ole32','Oleaut32','Advapi32','oleaut32','uuid','stdc++']
    OS_FLAGS='-DWINDOWS -D_CONSOLE -g -Wall'
    COMMON_CFLAGS=COMMON_CFLAGS+' -std=gnu99 '
    COMMON_CCFLAGS=COMMON_CCFLAGS+' -DWITH_DOUBLE_FLOAT -DUNICODE -D__MSVCRT_VERSION__=__MSVCR100_DLL ' 

但是这个思路有个弊端,那就是要求系统必须有指定版本的MSVCRxx.DLL,编译出来的程序可能无法顺利在其他电脑上运行,要不就是程序所在的目录必须附带指定版本的MSVCRxx.DLL。

第二个思路还是利用msvcrt.dll,这个动态库windows系统都有,而且MinGW默认也是链接它。打开C:\AWTK\SDK\awtk\src\platforms\pc\fs_os.c,搜索_stat64i32并替换为_stati64,保存。

顺利解决掉一个问题了,继续编译。还是scons

undefined reference to `WinMain@16'

这个错误很有意思,找不到WinMain函数,也找不到main函数。我们来看看C:\AWTK\SDK\awtk\tools\svg_gen\bsvg_gen.cc的源码

/**
 * File:   bsvg_gen.cc
 * Author: AWTK Develop Team
 * Brief:  bsvg_gen
 *
 * Copyright (c) 2018 - 2020  Guangzhou ZHIYUAN Electronics Co.,Ltd.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * License file for more details.
 *
 */

/**
 * History:
 * ================================================================
 * 2018-11-19 Li XianJing <xianjimli@hotmail.com> created
 *
 */

#include "tkc/mem.h"
#include "tkc/fs.h"
#include "common/utils.h"
#include "base/assets_manager.h"
#include "svg/svg_to_bsvg.h"

static ret_t bsvg_gen(const char* input_file, const char* output_file, bool_t bin) {
  uint32_t* out = NULL;
  uint32_t size = 0;
  uint32_t out_size = 0;
  char* xml = (char*)file_read(input_file, &size);

  if (svg_to_bsvg(xml, size, &out, &out_size) == RET_OK) {
    if (bin) {
      write_file(output_file, out, out_size);
    } else {
      output_res_c_source(output_file, ASSET_TYPE_IMAGE, ASSET_TYPE_IMAGE_BSVG, (uint8_t*)out,
                          out_size);
    }
  }

  TKMEM_FREE(xml);

  return RET_OK;
}

int wmain(int argc, wchar_t* argv[]) {
  bool_t output_bin = argc > 3;
  const char* in_filename = NULL;
  const char* out_filename = NULL;

  TKMEM_INIT(4 * 1024 * 1024)

  if (argc < 3) {
    printf("Usage: %S svg_filename bsvg_filename [bin]\n", argv[0]);
    return 0;
  }

  str_t in_file;
  str_t out_file;

  str_init(&in_file, 0);
  str_init(&out_file, 0);

  str_from_wstr(&in_file, argv[1]);
  str_from_wstr(&out_file, argv[2]);

  in_filename = in_file.str;
  out_filename = out_file.str;

  exit_if_need_not_update(in_filename, out_filename);

  bsvg_gen(in_filename, out_filename, output_bin);

  str_reset(&in_file);
  str_reset(&out_file);

  return 0;
}

#include "common/main.inc"

有个wmain函数,但这不是标准的入口函数。源码最后一行写着#include "common/main.inc",我们来看看C:\AWTK\SDK\awtk\tools\common\main.inc的源码

#include "common/utils.h"

#ifndef WIN32
int main(int argc, char* argv[]) {
  int ret = 0;
  wchar_t** argvw = argvw_create(argc, argv);

  ret = wmain(argc, argvw);
  argvw_destroy(argvw);

  return ret;
}
#endif /*WIN32*/

咦,这里有main函数呀,在main函数内部调用了wmain函数。难道是#ifndef WIN32造成的?从上文fs_os.c的报错来看,WIN32应该是定义过了,不然也不会报错

既然WIN32被定义过,那么#ifndef WIN32便不成立,main函数也不会被编译。看到这里,大体上可以猜测作者的意图是只有编译控制台程序时才编译main函数,但是你好歹给个WinMain函数呀。返回头来,咱们再看看bsvg_gen.cc是要实现什么功能的

#### 1.BSVG 生成工具。

```
./bin/bsvggen svg_filename bsvg_filename [bin]
```

* svg\_filename svg文件名。
* bsvg\_filename bsvg文件名。
* bin 是否生成二进制格式(目标平台有文件系统时使用),缺省生成C语言常量数组。

可见,这应该就是标准的控制台程序,我们尝试把#ifndef WIN32和#endif注释掉,再次编译看看。OK,顺利生成了bsvggen.exe。不过,又出现了新问题。

gcc -o bin\demo1.exe demos\demo1_app.o -Llib -lassets -lawtk -lextwidgets -lwidg
ets -lbase -lgpinyin -lminiz -ltkc -llinebreak -lnanovg-agge -lnanovg -lagge -lS
DL2 -lglad -lkernel32 -lgdi32 -luser32 -lwinmm -limm32 -lversion -lshell32 -lole
32 -lOleaut32 -lAdvapi32 -loleaut32 -luuid -lstdc++
c:/mingw/bin/../lib/gcc/mingw32/8.2.0/../../../../mingw32/bin/ld.exe: lib/libtkc
.a(event_source_manager_default.o): in function `_FD_ISSET':
c:/mingw/include/winsock.h:165: undefined reference to `__WSAFDIsSet@8'
c:/mingw/bin/../lib/gcc/mingw32/8.2.0/../../../../mingw32/bin/ld.exe: lib/libtkc
.a(event_source_manager_default.o): in function `event_source_manager_default_di
spatch_fds':
C:\AWTK\SDK\awtk/src/tkc/event_source_manager_default.c:79: undefined reference
to `select@20'
c:/mingw/bin/../lib/gcc/mingw32/8.2.0/../../../../mingw32/bin/ld.exe: lib/libtkc
.a(event_source_manager_default.o): in function `_FD_ISSET':
c:/mingw/include/winsock.h:165: undefined reference to `__WSAFDIsSet@8'
c:/mingw/bin/../lib/gcc/mingw32/8.2.0/../../../../mingw32/bin/ld.exe: c:/mingw/b
in/../lib/gcc/mingw32/8.2.0/../../../libmingw32.a(main.o):(.text.startup+0xb0):
undefined reference to `WinMain@16'
collect2.exe: error: ld returned 1 exit status
scons: *** [bin\demo1.exe] Error 1
scons: building terminated because of errors.

这里面有几个错误,我们分别来看。先看C:\AWTK\SDK\awtk/src/tkc/event_source_manager_default.c的源码

/**
 * File:   event_source_manager_default.c
 * Author: AWTK Develop Team
 * Brief:  event manager_default manager_default
 *
 * Copyright (c) 2019 - 2020  Guangzhou ZHIYUAN Electronics Co.,Ltd.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * License file for more details.
 *
 */

/**
 * History:
 * ================================================================
 * 2019-09-29 Li XianJing <xianjimli@hotmail.com> created
 *
 */

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif /*WIN32_LEAN_AND_MEAN*/

#include "tkc/mem.h"
#include "tkc/platform.h"
#include "tkc/event_source_manager_default.h"

#ifdef WITH_SOCKET

#ifdef WIN32
#include "windows.h"
#include <winsock2.h>
#include <ws2tcpip.h>
typedef int socklen_t;
#else
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#endif /*WIN32*/

static ret_t event_source_manager_default_dispatch_fds(event_source_manager_t* manager,
                                                       uint32_t sleep_time) {
  fd_set fdsr;
  uint32_t i = 0;
  uint32_t n = 0;
  int32_t fd = 0;
  int32_t ret = 0;
  int32_t max_fd = 0;
  struct timeval tv = {0, 0};
  event_source_t* iter = NULL;
  event_source_t** sources = NULL;
  return_value_if_fail(manager != NULL, 0);

  FD_ZERO(&fdsr);
  tv.tv_sec = sleep_time / 1000;
  tv.tv_usec = (sleep_time % 1000) * 1000;

  n = manager->dispatching_sources.size;
  sources = (event_source_t**)(manager->dispatching_sources.elms);

  for (i = 0; i < n; i++) {
    iter = sources[i];
    fd = event_source_get_fd(iter);
    if (fd >= 0) {
      FD_SET(fd, &fdsr);
      if (fd > max_fd) {
        max_fd = fd;
      }
    }
  }

  if (max_fd == 0) {
    return RET_OK;
  }

  ret = select(max_fd + 1, &fdsr, NULL, NULL, &tv);
  if (ret < 0) {
    perror("select");
    return RET_FAIL;
  } else if (ret == 0) {
    return RET_TIMEOUT;
  }

  for (i = 0; i < n; i++) {
    iter = sources[i];
    fd = event_source_get_fd(iter);

    if (fd >= 0) {
      if (FD_ISSET(fd, &fdsr)) {
        ret_t r = event_source_dispatch(iter);
        if (r == RET_REMOVE) {
          event_source_manager_remove(manager, iter);
        }
      }
    }
  }

  return RET_OK;
}
#else
static ret_t event_source_manager_default_dispatch_fds(event_source_manager_t* manager,
                                                       uint32_t sleep_time) {
  return RET_OK;
}
#endif /*WITH_SOCKET*/

还是从报错情况来分析,#ifdef WITH_SOCKET和#ifdef WIN32应该都是成立的(在awtk_config.py中有WITH_SOCKET的定义),那么必定有

#include "windows.h"
#include <winsock2.h>
#include <ws2tcpip.h>

然而在MinGW中,要想使用winsock2,应该在链接时添加参数-lws2_32来链接到ws2_32.dll,所以解决这里的问题也有两个思路,要么添加链接参数-lws2_32,要么在awtk_config.py去掉WITH_SOCKET的定义。我们这里采用前者,用记事本打开C:\AWTK\SDK\awtk\awtk_config.py,找到

  elif TOOLS_NAME == 'mingw' :
    OS_LIBS=['kernel32', 'gdi32', 'user32', 'winmm','imm32','version','shell32','ole32','Oleaut32','Advapi32','oleaut32','uuid','stdc++']

修改为

  elif TOOLS_NAME == 'mingw' :
    OS_LIBS=['kernel32', 'gdi32', 'user32', 'winmm','imm32','version','shell32','ole32','Oleaut32','Advapi32','oleaut32','uuid','stdc++','ws2_32']

再编译一下试试

gcc -o bin\demo1.exe demos\demo1_app.o -Llib -lassets -lawtk -lextwidgets -lwidg
ets -lbase -lgpinyin -lminiz -ltkc -llinebreak -lnanovg-agge -lnanovg -lagge -lS
DL2 -lglad -lkernel32 -lgdi32 -luser32 -lwinmm -limm32 -lversion -lshell32 -lole
32 -lOleaut32 -lAdvapi32 -loleaut32 -luuid -lstdc++ -lws2_32
c:/mingw/bin/../lib/gcc/mingw32/8.2.0/../../../../mingw32/bin/ld.exe: c:/mingw/b
in/../lib/gcc/mingw32/8.2.0/../../../libmingw32.a(main.o):(.text.startup+0xb0):
undefined reference to `WinMain@16'
collect2.exe: error: ld returned 1 exit status
scons: *** [bin\demo1.exe] Error 1
scons: building terminated because of errors.

嗯。。。错误似曾相识(上文)。还是从源码入手,先看C:\AWTK\SDK\awtk\demos\demo1_app.c,最关键的还是这一句

#include "awtk_main.inc"

打开C:\AWTK\SDK\awtk\src\awtk_main.inc

/**
 * File:   awtk_main.c
 * Author: AWTK Develop Team
 * Brief:  awtk main
 *
 * Copyright (c) 2018 - 2020  Guangzhou ZHIYUAN Electronics Co.,Ltd.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * License file for more details.
 *
 */

/**
 * History:
 * ================================================================
 * 2018-02-16 Li XianJing <xianjimli@hotmail.com> created
 *
 */

#include "awtk.h"

BEGIN_C_DECLS
extern ret_t assets_init();
END_C_DECLS

#ifndef LCD_WIDTH
#define LCD_WIDTH 320
#endif /*LCD_WIDTH*/

#ifndef LCD_HEIGHT
#define LCD_HEIGHT 480
#endif /*LCD_HEIGHT*/

#ifndef APP_TYPE
#define APP_TYPE APP_SIMULATOR
#endif /*APP_TYPE*/

#ifndef GLOBAL_INIT
#define GLOBAL_INIT()
#endif /*GLOBAL_INIT*/

#ifndef GLOBAL_EXIT
#define GLOBAL_EXIT()
#endif /*GLOBAL_EXIT*/

#ifndef APP_NAME
#define APP_NAME "awtk"
#endif /*APP_NAME*/

#ifndef APP_RES_ROOT
#define APP_RES_ROOT NULL
#endif /*APP_RES_ROOT*/

#ifdef IOS
#include "base/asset_loader_zip.h"

#endif /*IOS*/
#ifdef USE_GUI_MAIN
int gui_app_start(int lcd_w, int lcd_h) {
  tk_init(lcd_w, lcd_h, APP_MOBILE, APP_NAME, APP_RES_ROOT);
#elif defined(MOBILE_APP) && defined(WITH_SDL)
int SDL_main(int argc, char* argv[]) {
  int32_t lcd_w = 0;
  int32_t lcd_h = 0;

  tk_init(lcd_w, lcd_h, APP_MOBILE, APP_NAME, APP_RES_ROOT);
  system_info_set_default_font(system_info(), "default_full");

#elif defined(WIN32)
#include <windows.h>
#define MAX_ARGV 7

static void command_line_to_argv(char* lpcmdline, const char* argv[MAX_ARGV], int32_t* argc) {
  int32_t i = 1;
  char* p = lpcmdline;

  argv[0] = "awtk.exe";

  if (!*p) {
    argv[1] = NULL;
    return;
  }

  for (i = 1; i < MAX_ARGV; i++) {
    argv[i] = p;
    if (*p == '\"') {
      argv[i] = p + 1;
      p = strchr(p + 1, '\"');
      if (p == NULL) break;
      *p++ = '\0';
      if (*p == 0) break;
    } else {
      p = strchr(p, ' ');
    }
    if (p == NULL) {
      break;
    }

    while (*p == ' ') {
      *p++ = '\0';
    }
  }
  *argc = i + 1;

  return;
}

int WINAPI wWinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPWSTR lpcmdline, int ncmdshow) {
  str_t str;
  int argc = 1;
  char* argv[MAX_ARGV];
  int32_t lcd_w = LCD_WIDTH;
  int32_t lcd_h = LCD_HEIGHT;

  str_init(&str, 1024);
  str_from_wstr(&str, lpcmdline);
  command_line_to_argv(str.str, argv, &argc);

  TK_ENABLE_CONSOLE();

#ifdef ON_CMD_LINE
  ON_CMD_LINE(argc, argv);
#else
  if (argc >= 2) {
    lcd_w = tk_atoi(argv[1]);
  }
  if (argc >= 3) {
    lcd_h = tk_atoi(argv[2]);
  }
#endif /*ON_CMD_LINE*/

  tk_init(lcd_w, lcd_h, APP_TYPE, APP_NAME, APP_RES_ROOT);
#else
int main(int argc, char* argv[]) {
  int32_t lcd_w = LCD_WIDTH;
  int32_t lcd_h = LCD_HEIGHT;
#ifdef ON_CMD_LINE
  ON_CMD_LINE(argc, argv);
#else
  if (argc >= 2) {
    lcd_w = tk_atoi(argv[1]);
  }
  if (argc >= 3) {
    lcd_h = tk_atoi(argv[2]);
  }
#endif /*ON_CMD_LINE*/

  tk_init(lcd_w, lcd_h, APP_TYPE, APP_NAME, APP_RES_ROOT);
#endif

#ifdef ASSETS_ZIP
  assets_manager_set_loader(assets_manager(), asset_loader_zip_create(ASSETS_ZIP));
#endif /*ASSETS_ZIP*/

#if defined(WITH_LCD_PORTRAIT)
  if (lcd_w > lcd_h) {
    tk_set_lcd_orientation(LCD_ORIENTATION_90);
  }
#endif /*WITH_LCD_PORTRAIT*/

#ifdef WITH_LCD_LANDSCAPE
  if (lcd_w < lcd_h) {
    tk_set_lcd_orientation(LCD_ORIENTATION_90);
  }
#endif /*WITH_LCD_PORTRAIT*/

#ifdef WITH_FS_RES
  system_info_set_default_font(system_info(), "default_full");
#endif /*WITH_FS_RES*/

  assets_init();

#ifndef WITHOUT_EXT_WIDGETS
  tk_ext_widgets_init();
#endif/*WITHOUT_EXT_WIDGETS*/

  log_set_log_level(LOG_LEVEL_INFO);
  log_info("Build at: %s %s\n", __DATE__, __TIME__);

#ifdef ENABLE_CURSOR
  window_manager_set_cursor(window_manager(), "cursor");
#endif /*ENABLE_CURSOR*/

  GLOBAL_INIT();
  application_init();
  tk_run();
  application_exit();
  GLOBAL_EXIT();

#ifdef WIN32
  str_reset(&str);
#endif /*WIN32*/

#ifdef HAS_STDIO
  fflush(stdout);
#endif /*HAS_STDIO*/

  return 0;
}

我们把代码整理一下,单独看入口函数

#ifdef USE_GUI_MAIN
int gui_app_start(int lcd_w, int lcd_h) {

#elif defined(MOBILE_APP) && defined(WITH_SDL)
int SDL_main(int argc, char* argv[]) {

#elif defined(WIN32)
int WINAPI wWinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPWSTR lpcmdline, int ncmdshow) {

#else
int main(int argc, char* argv[]) {

这下看的就比较明晰了,gui_app_start()是用于无操作系统的裸机程序,SDL_main()用于手机APP,wWinMain()用于win32程序,main()用于控制台程序。在这些条件编译里面,成立的显然是#elif defined(WIN32),但为何提示找不到入口函数呢?难道是编译器不认识wWinMain()函数?嗯。。。还真的是,wWinMain()函数是WinMain()宽字符版,下面引用https://www.cnblogs.com/Dageking/archive/2014/01/15/3520406.html的一段描述

在Windows环境中,编译器的翻译策略是GB2312到UCS-2BE;Linux环境中的策略是UTF-8到UTF-32BE。
这时候就要求源文件的编码与编译器的本地化策略集中代码翻译的策略一致,例如VC只能读取GB2312的源代码(这里还是例外,VC太自作聪明了 ,会将很多其他代码在编译时自动转换成GB2312),而gcc只能读取UTF-8的源代码(这里就有个尴尬,MinGW运行win32下,所以只有 GB2312系统才认;而MinGW却用gcc编写,所以自己只认UTF-8,所以结果就是,MinGW的宽字符被废掉了)。

看来是MinGW在windows下不支持宽字符,那我们还是用WinMain()好了。打开C:\AWTK\SDK\awtk\src\awtk_main.inc,将wWinMain()函数修改为

int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) {

问题顺利解决,再次编译,生成了一堆demo程序,运行一个看看

不过,还是有新问题

gcc -o bin\preview_ui.exe demos\preview_ui.o -Llib -lassets -lawtk -lextwidgets
-lwidgets -lbase -lgpinyin -lminiz -ltkc -llinebreak -lnanovg-agge -lnanovg -lag
ge -lSDL2 -lglad -lkernel32 -lgdi32 -luser32 -lwinmm -limm32 -lversion -lshell32
 -lole32 -lOleaut32 -lAdvapi32 -loleaut32 -luuid -lstdc++ -lws2_32
c:/mingw/bin/../lib/gcc/mingw32/8.2.0/../../../../mingw32/bin/ld.exe: c:/mingw/b
in/../lib/gcc/mingw32/8.2.0/../../../libmingw32.a(main.o):(.text.startup+0xb0):
undefined reference to `WinMain@16'
collect2.exe: error: ld returned 1 exit status
scons: *** [bin\preview_ui.exe] Error 1
scons: building terminated because of errors.

说是新问题,其实老问题,哈哈。修改原理同上。

5、直到看到这一句

才真正表明编译完全通过。

让人感到不解的是,有两个编译出来的程序竟然被卡巴斯基杀掉了,晕。。。

6、后记

为了加快编译速度,可以使用多线程编译,在scons后加参数-jx,x代表线程数,在使用多线程编译前需要安装pywin32。

pip install pywin32

7、后记的后记

这篇文章还没写完,ZLG官方已修复上述问题(我反馈的),但是必须到github更新最新源码,AWTK-Designer-x64-0.1.3-Preview-Setup安装包自带的SDK(1.3版)还是存在以上问题。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
AWTK开发手册-AWTK开发实践指南-中文手册.pdf AWTK = Toolkit AnyWhere 随着手机、智能手表等便携式设备的普及,用户对 GUI 的要求越来越高,嵌入式系统对高性能、高可靠性、低功耗、美观炫酷的 GUI 的需求也越来越迫切,ZLG开源 GUI 引擎 AWTK 应运而生。AWTK 全称为 Toolkit AnyWhere,是 ZLG 倾心打造的一套基于 C 语言开发的 GUI 框架。旨在为用户提供一个功能强大、高效可靠、简单易用、可轻松做出炫酷效果的 GUI 引擎,并支持跨平台同步开发,一次编程,终生使用。 最终目标: 支持开发嵌入式软件。 支持开发Linux应用程序。 支持开发MacOS应用程序。 支持开发Windows应用程序。 支持开发Android应用程序。 支持开发iOS应用程序。 支持开发2D游戏。 其主要特色有: 小巧。在精简配置下,不依赖第三方软件包,仅需要32K RAM + 256K FLASH即可开发一些简单的图形应用程序。 高效。采用脏矩形裁剪算法,每次只绘制和更新变化的部分,极大提高运行效率和能源利用率。 稳定。通过良好的架构设计和编程风格、单元测试、动态(valgrind)检查和Code Review保证其运行的稳定性。 丰富的GUI组件。提供窗口、对话框和各种常用的组件(用户可以配置自己需要的组件,降低对运行环境的要求)。 支持多种字体格式。内置位图字体(并提供转换工具),也可以使用stb_truetype或freetype加载ttf字体。 支持多种图片格式。内置位图图片(并提供转换工具),也可以使用stb_image加载png/jpg等格式的图片。 紧凑的二进制界面描述格式。可以手工编辑的XML格式的界面描述文件,也可以使用Qt Designer设计界面,然后转换成紧凑的二进制界面描述格式,提高运行效率,减小内存开销。 支持主题并采用紧凑的二进制格式。开发时使用XML格式描述主题,然后转换成紧凑的二进制格式,提高运行效率,减小内存开销。 支持裸系统,无需OS和文件系统。字体、图片、主题和界面描述数据都编译到代码中,以常量数据的形式存放,运行时无需加载到内存。 内置nanovg实现高质量的矢量动画,并支持SVG矢量图。 支持窗口动画、控件动画、滑动动画和高清LCD等现代GUI常见特性。 支持国际化(Unicode、字符串翻译和输入法等)。 可移植。支持移植到各种RTOS和嵌入式Linux系统,并通过SDL在各种流行的PC/手机系统上运行。 脚本化。从API注释中提取API的描述信息,通过这些信息可以自动生成各种脚本的绑定代码。 支持硬件2D加速(目前支持STM32的DMA2D和NXP的PXP)和GPU加速(OpenGL/OpenGLES/DirectX/Metal),充分挖掘硬件潜能。 丰富的文档和示例代码。 采用LGPL协议开源发布,在商业软件中使用时无需付费。 目前核心功能已经完成,内部开始在实际项目中使用了,欢迎有兴趣的朋友评估和尝试,期待您的反馈。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大鹏集成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值