天天看點

用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槽。

用MinGW編譯AWTK填坑錄

AWTK-Designer安裝完成後,可以在SDK檔案夾下找到AWTK的全部源碼,無需另外下載下傳,但有可能不是最新的版本,如果需要最新的源碼,需要到主源碼倉庫:https://github.com/zlgopen/awtk  進行更新。

2、從python官網下載下傳64位的安裝包,位址:https://www.python.org/downloads/windows/

用MinGW編譯AWTK填坑錄

輕按兩下安裝包,在 Add Python 3.8 to PATH 前打勾,然後選擇自定義安裝。

用MinGW編譯AWTK填坑錄

修改安裝路徑

用MinGW編譯AWTK填坑錄

其他選項保持預設。

下載下傳scons,位址:https://scons.org/pages/download.html

用MinGW編譯AWTK填坑錄

解壓到任意目錄,打開CMD,進入scons目錄,輸入

python setup.py install
           

進行安裝。

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

将MinGW檔案夾解壓到C槽,然後添加環境變量。

用MinGW編譯AWTK填坑錄
用MinGW編譯AWTK填坑錄

CMD視窗輸入

gcc -v
           

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

用MinGW編譯AWTK填坑錄

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呢?

用MinGW編譯AWTK填坑錄

由此可見,我的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,儲存。

用MinGW編譯AWTK填坑錄

順利解決掉一個問題了,繼續編譯。還是scons

用MinGW編譯AWTK填坑錄
undefined reference to `[email protected]'
           

這個錯誤很有意思,找不到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 <[email protected]> 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應該是定義過了,不然也不會報錯

用MinGW編譯AWTK填坑錄

既然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 `[email protected]'
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 `[email protected]'
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 `[email protected]'
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 `[email protected]'
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 <[email protected]> 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 `[email protected]'
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 <[email protected]> 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()函數修改為

問題順利解決,再次編譯,生成了一堆demo程式,運作一個看看

用MinGW編譯AWTK填坑錄

不過,還是有新問題

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 `[email protected]'
collect2.exe: error: ld returned 1 exit status
scons: *** [bin\preview_ui.exe] Error 1
scons: building terminated because of errors.
           

說是新問題,其實老問題,哈哈。修改原理同上。

5、直到看到這一句

用MinGW編譯AWTK填坑錄

才真正表明編譯完全通過。

讓人感到不解的是,有兩個編譯出來的程式竟然被卡巴斯基殺掉了,暈。。。

用MinGW編譯AWTK填坑錄

6、後記

為了加快編譯速度,可以使用多線程編譯,在scons後加參數-jx,x代表線程數,在使用多線程編譯前需要安裝pywin32。

pip install pywin32
           

7、後記的後記

這篇文章還沒寫完,ZLG官方已修複上述問題(我回報的),但是必須到github更新最新源碼,AWTK-Designer-x64-0.1.3-Preview-Setup安裝包自帶的SDK(1.3版)還是存在以上問題。