找回密码
 立即注册
首页 业界区 业界 AWTK项目编译问题整理(1)

AWTK项目编译问题整理(1)

红弘丽 昨天 10:51
三方库组织

公司的项目初步三方库路径组织是这样,awtk-widget开头的是awtk的自定义控件,无源码的二进制库放在sourceless这个文件夹:
  1. ./3rd<br>    ├── awtk-widget-battery-widget<br>    ├── awtk-widget-border-text<br>    ├── awtk-widget-option-box<br>    ├── awtk-widget-range-rule-widget<br>    ├── awtk-widget-range-slider<br>    ├── awtk-widget-sonar-image<br>    └── sourceless
复制代码
sourceless文件夹下,分不同平台版本,平台文件夹下集合所有依赖库。每个依赖库我都按一个文件夹整理,include文件夹存放头文件,bin文件夹存放so库(为什么是bin不是lib?因为awtk内置的拷贝库脚本定死了只找bin文件夹下的库)。
  1. project/3rd/sourceless/ubuntu$ tree -L 2<br>.<br>├── cairo<br>│   ├── bin<br>│   └── include<br>├── freetype<br>│   ├── bin<br>│   ├── include<br>│   └── share<br>├── libjpeg-turbo<br>│   ├── bin<br>│   └── include<br>├── libpng<br>│   ├── bin<br>│   ├── include<br>│   └── share<br>├── openssl<br>│   ├── bin<br>│   └── include<br>├── pixman<br>│   ├── bin<br>│   └── include<br>├── poco<br>│   ├── bin<br>│   └── include<br>├── version.md<br>└── zlib<br>    ├── bin<br>    ├── include<br>    └── share<br>​
复制代码
为什么这样放?估计当时觉得一个库就是一个package, 看着比较整洁。
这种堆放方式后面证明对项目维护十分麻烦,对于上面的cairo, poco, libjpeg, zlib等库,每一个包我都要在SConscript里面指明路径:
  1. SOURCE_3RD_DEPS_PATH = os.path.normpath(os.path.join(os.getcwd(), '3rd/sourceless'))<br>​<br>if PLATFORM == 'Linux':<br>    if LINUX_FB == True:<br>        SOURCE_3RD_DEPS_PATH = (os.path.join(SOURCE_3RD_DEPS_PATH, 'T113'))<br>    else:<br>         SOURCE_3RD_DEPS_PATH = (os.path.join(SOURCE_3RD_DEPS_PATH, 'ubuntu'))<br>elif PLATFORM == 'Windows':<br>    SOURCE_3RD_DEPS_PATH = os.path.join(SOURCE_3RD_DEPS_PATH, 'win')<br>    <br>DEPENDS_LIBS += [<br>   {<br>            'root' : os.path.join(SOURCE_3RD_DEPS_PATH, 'cairo'),<br>            'shared_libs' : ['cairo']<br>        },<br>        {<br>            'root' : os.path.join(SOURCE_3RD_DEPS_PATH, 'freetype'),<br>            'shared_libs' : ['freetype']<br>        },<br>        {<br>            'root' : os.path.join(SOURCE_3RD_DEPS_PATH, 'libjpeg-turbo'),<br>            'shared_libs' : ['jpeg', 'turbojpeg']<br>        },<br>        {<br>            'root' : os.path.join(SOURCE_3RD_DEPS_PATH, 'pixman'),<br>            'shared_libs': ['pixman-1']<br>        },<br>        {<br>            'root' : os.path.join(SOURCE_3RD_DEPS_PATH, 'libpng'),<br>            'shared_libs' : ['png16']<br>        },<br>        {<br>            'root' : os.path.join(SOURCE_3RD_DEPS_PATH, 'zlib'),<br>            'shared_libs' : ['z']<br>        },<br>        {<br>            'root': os.path.join(SOURCE_3RD_DEPS_PATH, 'openssl'),<br>            'shared_libs': ['crypto', 'ssl']<br>        },<br>        {<br>            'root' : os.path.join(SOURCE_3RD_DEPS_PATH, 'poco'),<br>            'shared_libs' : ['PocoFoundation', 'PocoUtil', 'PocoXML', 'PocoJSON', 'PocoCrypto', 'PocoNet', 'PocoNetSSL']<br>        }<br>]<br>...<br>​<br>helper.set_deps(DEPENDS_LIBS)
复制代码
这样每加一个库我都要更新一次DEPEND_LIBS,几个库的小项目还好说,要是几十个库还得了。
awtk的helper.set_deps()函数可以自动把shared_libs指定的库列表拷贝过去,但一些情景也有失灵的时候,只能手动拷贝,这种结构一个个文件夹去拷贝肯定特别酸爽。
后面我参考linux内对头文件和so文件的组织方法,把头文件和库文件夹都扁平化了:
  1. project/3rd/binary/ubuntu$ tree -L 2<br>.<br>├── bin<br>│   ├── addsymlinks.sh<br>│   ├── libcairo.so -> libcairo.so.2<br>│   ├── libcairo.so.2 -> libcairo.so.2.11600<br>│   ├── libcairo.so.2.11600 -> libcairo.so.2.11600.0<br>│   ├── libcairo.so.2.11600.0<br>│   ├── libcrypto.so -> libcrypto.so.1<br>│   ├── libcrypto.so.1 -> libcrypto.so.1.1<br>│   ├── libcrypto.so.1.1<br>│   ├── libfreetype.so -> libfreetype.so.6<br>│   ├── libfreetype.so.6 -> libfreetype.so.6.20<br>│   ├── libfreetype.so.6.20 -> libfreetype.so.6.20.0<br>│   ├── libfreetype.so.6.20.0<br>│   ├── libjpeg.so -> libjpeg.so.62<br>│   ├── libjpeg.so.62 -> libjpeg.so.62.4<br>│   ├── libjpeg.so.62.4 -> libjpeg.so.62.4.0<br>│   ├── libjpeg.so.62.4.0<br>....<br>├── include<br>│   ├── cairo<br>│   ├── cJSON.h<br>│   ├── evconfig-private.h<br>│   ├── event2<br>│   ├── freetype2<br>│   ├── jconfig.h<br>│   ├── jerror.h<br>│   ├── jmorecfg.h<br>│   ├── jpeglib.h<br>│   ├── libpng16<br>│   ├── mosquitto_broker.h<br>│   ├── mosquitto.h<br>....<br>└── VERSION.md
复制代码
这样后面加三方库只需要CV include和bin就行了,不用修改SConstruct, 要手动拷贝直接cp -P xxx/bin/* *就行。
  1. THIRD_SRC = os.path.join(os.getcwd(), '3rd/')<br>BINARY_SRC = os.path.join(THIRD_SRC, 'binary')<br>if PLATFORM == 'Linux':<br>    if LINUX_FB == True:<br>        BINARY_SRC = (os.path.join(BINARY_SRC, 't113'))<br>    else:<br>        BINARY_SRC = (os.path.join(BINARY_SRC, 'ubuntu'))<br>​<br>BINARY_LIB_PATH = [<br>    os.path.join(BINARY_SRC, 'bin')<br>]<br>​<br>BINARY_LIB_NAMES = ["mosquitto", "mosquittopp", "nanomsg"] <br>BINARY_LIB_NAMES += ["cairo", "freetype", "jpeg", "turbojpeg", "pixman-1", "png16", "z", "crypto", "ssl"]<br>BINARY_LIB_NAMES += ["PocoFoundation", "PocoUtil", "PocoXML", "PocoJSON", "PocoCrypto", "PocoNet", "PocoNetSSL"]<br>​<br>​<br>DEPENDS_LIBS += [<br>   {<br>        "root" : os.path.join(BINARY_LIB_PATH, ''),<br>        'shared_libs': BINARY_LIB_NAMES,<br>        'static_libs': []<br>   }<br>]  
复制代码
项目连带自定义控件一起编译

需要加这么一行,不然scons只会编译项目本身:
  1. app.prepare_depends_libs(ARGUMENTS, helper, DEPENDS_LIBS)
复制代码
这个方法也适用于其他基于Sconstruct的三方库项目。
不同平台区分文件夹输出

awtk的scons编译默认会把执行文件输出到bin文件夹,而.o文件会和源代码混在一起。
这样涉及跨平台编译会有个问题,由于不管什么平台到默认输出到bin文件夹,而后一个平台的编译输出会覆盖前一个平台的编译输出,导致如果编了机器版本的软件,pc版本的软件就没法用了,得重编,反之亦然,不方便同时看测试效果。
上网查了下scons的SConscript有个variant_dir参数可以输出build文件夹位置,然后我就去看awtk对于scons的封装脚本,果然在app_helper_base.py看到这么一段:
  1. def SConscript(self, SConscriptFiles):<br>        if not self.BUILD_DIR:<br>            Script.SConscript(SConscriptFiles)<br>        else:<br>            env = Environment.Environment()<br>            env.Default(self.BUILD_DIR)<br>            for sc in SConscriptFiles:<br>                dir = os.path.dirname(sc)<br>                build_dir = os.path.join(self.BUILD_DIR, dir)<br>                Script.SConscript(sc, variant_dir=build_dir, duplicate=False)
复制代码
其中self.BUILD_DIR是awtk可通过ARGUMENTS指定的环境参数。
在项目的SConstrct里可以这么加,我的情况是需要跨windows, ubuntu, 嵌入式linux机器三个平台:
  1. PLATFORM = platform.system()<br>if PLATFORM == 'Linux':<br>    if LINUX_FB == True:<br>        ARGUMENTS['BUILD_DIR'] = 'build_linux_fb'<br>    else:<br>        ARGUMENTS['BUILD_DIR'] = 'build_ubuntu'<br>elif PLATFORM == 'Windows':<br>    ARGUMENTS['BUILD_DIR'] = 'build_windows'<br>​
复制代码
测试环境为ubuntu20.04, 现在在项目根目录分别执行scons和scons LINUX_FB=true, 就能看到两个平台的输出分别被放到build_ubuntu和build_linux_fb文件夹了。
  1. ├── build_linux_fb<br>│   ├── bin<br>│   ├── lib<br>│   ├── src<br>│   └── tests<br>├── build_ubuntu<br>│   ├── bin<br>│   ├── lib<br>│   ├── src<br>│   └── tests
复制代码
不过也有副作用,如果加了三方库的话,对于之前用helper.set_deps(DEPENDS_LIBS) , DEPEND_LIBS里指定的每个三方库文件夹三方库必须被放在BUILD_DIR下,也就是我现在对于之前的二进制库不得不改成这样:
  1. project/3rd/binary$ tree -L 2<br>.<br>├── build_linux_fb<br>│   ├── bin<br>│   ├── include<br>│   └── VERSION.md<br>└── build_ubuntu<br>    ├── bin<br>    ├── include<br>    └── VERSION.md
复制代码
同时项目的scripts/release.py需要修改输出路径,默认是写死bin的。
  1. OS_NAME = platform.system()<br>PRJ_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))<br>OUTPUT_DIR = join_path(PRJ_DIR, 'release')<br># old<br># BIN_DIR = join_path(PRJ_DIR, 'bin')<br># new<br>BIN_DIR = join_path(PRJ_DIR, 'build_linux_fb/bin')
复制代码
三方库软链接问题

项目用到mosquitto和poco两个库,编译的时候发现bin/ld报找不到库:
  1. /bin/ld: cannot find -lmosquitto<br>/bin/ld: cannot find -lmosquittopp<br>/bin/ld: cannot find -lnanomsg<br>/bin/ld: cannot find -lPocoFoundation<br>/bin/ld: cannot find -lPocoUtil<br>/bin/ld: cannot find -lPocoXML<br>/bin/ld: cannot find -lPocoJSON<br>/bin/ld: cannot find -lPocoCrypto<br>/bin/ld: cannot find -lPocoNet<br>/bin/ld: cannot find -lPocoNetSSL
复制代码
工作由于赶工,一时半会找不到办法,只好把用到的库都拷贝到/usr/local/lib去,但毕竟会污染环境,每次部署到一个新环境就得这么做一次,不是什么好方法。
有时间后开始探索,检查编译log,明明-L路径已经指明到有库的路径3rd/binary/build_ubuntu/bin:
  1. scons: Building targets ...<br>g++ -o build_ubuntu/bin/demo -Wl,-rpath=./bin -Wl,-rpath=./ -Wl,-rpath=/project/build_ubuntu/bin build_ubuntu/src/common/battery.o -L3rd/binary/build_ubuntu/bin -L3rd/binary/build_ubuntu/lib -lmosquitto -lmosquittopp -lnanomsg -lPocoFoundation -lPocoUtil -lPocoXML -lPocoJSON -lPocoCrypto -lPocoNet -lPocoNetSSL -lsetting_slider -lsonar_image -lrange_slider -lrange_rule_widget -loption_box -lborder_text -lbattery_widget -lmvvm -lawtk -lGL -lgtk-3 -lgdk-3 -lglib-2.0 -lgobject-2.0 -lXext -lX11 -lsndio -lstdc++ -lasound -lpthread -lm -ldl
复制代码
百思不得上网搜索,查到了ld --verbose可以输出链接库的查找过程,于是我试了下输出:
  1. ld -lPocoFoundation --verbose<br>中间略去一堆....<br>==================================================<br>ld: mode elf_x86_64<br>attempt to open /usr/local/lib/x86_64-linux-gnu/libPocoFoundation.so failed<br>attempt to open /usr/local/lib/x86_64-linux-gnu/libPocoFoundation.a failed<br>attempt to open /lib/x86_64-linux-gnu/libPocoFoundation.so failed<br>attempt to open /lib/x86_64-linux-gnu/libPocoFoundation.a failed<br>attempt to open /usr/lib/x86_64-linux-gnu/libPocoFoundation.so failed<br>attempt to open /usr/lib/x86_64-linux-gnu/libPocoFoundation.a failed<br>attempt to open /usr/lib/x86_64-linux-gnu64/libPocoFoundation.so failed<br>attempt to open /usr/lib/x86_64-linux-gnu64/libPocoFoundation.a failed<br>attempt to open /usr/local/lib64/libPocoFoundation.so failed<br>attempt to open /usr/local/lib64/libPocoFoundation.a failed<br>attempt to open /lib64/libPocoFoundation.so failed<br>attempt to open /lib64/libPocoFoundation.a failed<br>attempt to open /usr/lib64/libPocoFoundation.so failed<br>attempt to open /usr/lib64/libPocoFoundation.a failed<br>attempt to open /usr/local/lib/libPocoFoundation.so failed<br>attempt to open /usr/local/lib/libPocoFoundation.a failed<br>attempt to open /lib/libPocoFoundation.so failed<br>attempt to open /lib/libPocoFoundation.a failed<br>attempt to open /usr/lib/libPocoFoundation.so failed<br>attempt to open /usr/lib/libPocoFoundation.a failed<br>attempt to open /usr/x86_64-linux-gnu/lib64/libPocoFoundation.so failed<br>attempt to open /usr/x86_64-linux-gnu/lib64/libPocoFoundation.a failed<br>attempt to open /usr/x86_64-linux-gnu/lib/libPocoFoundation.so failed<br>attempt to open /usr/x86_64-linux-gnu/lib/libPocoFoundation.a failed
复制代码
原来定死了是从无后缀的.so开始找的,无后缀so通常都是指向带后缀版本so库的软链接,而我的lib路径下都是带版本后缀的,反而没有软链接,估计是当时源码编译后直接CV手动拷贝,于是那些软链接就丢失了,后面才查到应该用cp -P去拷贝。
一个个ln -s去给这些so库建链接肯定不是什么好办法,遂在网上寻找可以自动建链接的脚本,果然找到一个, 自己学习过程加工了下,如下:
  1. #!/bin/bash<br># liblinks - generate symbolic links <br># given libx.so.0.0.0 this would generate links for libx.so.0.0, libx.so.0, libx.so<br>#<br># ref: https://stackoverflow.com/questions/462100/bash-script-to-create-symbolic-links-to-shared-libraries<br>LIBFILES=`ls lib*.so.*`<br>for FILE in $LIBFILES;<br>   do<br>    shortlib=$FILE<br>    basename=$FILE<br>    echo "=================In loop========================"<br>    while extn=$(echo $shortlib | sed -n '/\.[0-9][0-9]*$/s/.*\(\.[0-9][0-9]*\)$/\1/p')<br>        echo "basename: $basename extn:$extn"<br> <br>          [ -n "$extn" ]<br>    <br>    do<br>        shortlib=$(basename $shortlib $extn)<br>        echo "ln -fs $basename $shortlib"<br>        ln -fs $basename $shortlib<br>        basename=$shortlib<br>    done<br>    echo "=====================Out loop=================="<br>   done
复制代码
将脚本命名为addsymlink.sh,放到三方库bin文件夹中,执行,然后回到项目根目录scons编译,就没再报bin/ld的问题了,看来就是无软链接所致,当前在windows上只知道dll不知道lib的教训在linux上又演了一把。
 
不过这个方法并没有完美解决所有问题,有些库十分顽固,必须提供带版本号的版本,否则就无法运行:
  1. kp25s_expo$ ./bin/demo <br>./bin/demo: error while loading shared libraries: libjpeg.so.62: cannot open shared object file: No such file or directory
复制代码
Poco库同样带版本号就没有这种现象,ldd一查,发现Poco库已经自动链接到我存放的库路径了,libjpeg库就没有,不知道是我设了什么环境变量导致还是库的特性。
  1. ldd ./bin/demo <br>        linux-vdso.so.1 (0x00007fff637d0000)<br>        libPocoFoundation.so.64 => /home/zhangdalin/AWStudioProjects/project/3rd/sourceless/ubuntu/poco/bin/libPocoFoundation.so.64 (0x00007f15a20d5000)<br>        libPocoNet.so.64 => /home/zhangdalin/AWStudioProjects/project/3rd/sourceless/ubuntu/poco/bin/libPocoNet.so.64 (0x00007f15a1f94000)<br>        libPocoNetSSL.so.64 => /home/zhangdalin/AWStudioProjects/project/3rd/sourceless/ubuntu/poco/bin/libPocoNetSSL.so.64 (0x00007f15a1f47000)<br>        libjpeg.so.62 => not found
复制代码
目前也想不到很好的解决方法,zlib和libjpeg都有这种问题,还好出问题的库就一两个,在机器打包后可以直接ln -s libxx.so libxx.so.x给这些库建软链接解决问题,要是大量库都是这种情况只能cp -P大法了。
(2025.1.24 ADD 上述问题已经得到解决,见:https://github.com/zlgopen/awtk/issues/895)
找到两个解决方法: 1.自己写拷贝函数,指定文件夹把库拷过去:
  1. import os<br>import shutil<br>import subprocess<br>import sys<br>import glob<br>import platform<br><br>def copy_binary_libs_to_build_dir(dst_path, lib_list, src_path):<br>    """<br>    将 dst_path 中匹配 lib_list 的文件拷贝到 src_path 文件夹。<br>    对于 Linux 系统,会查找 lib_list 中对应元素的 .so 文件是否存在,<br>    如果不存在就调用 dst_path 中的 addsymlink.sh 更新软链接。<br>    Linux 系统情况下,将拷贝对应元素所有的匹配 .so* 的文件(包括软链接和带版本号后缀的 so 文件)。<br>    """<br>    PLATFORM = platform.system()<br>    # 确保目标目录存在<br>    os.makedirs(src_path, exist_ok=True)<br><br>    for lib_name in lib_list:<br>        # 根据平台确定库文件的后缀<br>        if PLATFORM == 'Windows':<br>            lib_file = f'{lib_name}.dll'<br>            src_lib_path = os.path.join(dst_path, lib_file)<br>            dest_lib_path = os.path.join(src_path, lib_file)<br><br>            # 检查库文件是否存在<br>            if not os.path.exists(src_lib_path):<br>                print(f"Warning: Library {lib_file} not found in {dst_path}.")<br>                continue<br><br>            # 拷贝文件<br>            shutil.copy2(src_lib_path, dest_lib_path)<br>            print(f"Copied {src_lib_path} to {dest_lib_path}.")<br><br>        elif PLATFORM == 'Linux':<br>            # Linux 系统下,查找 lib_name 对应的 .so 文件<br>            lib_file_pattern = f'lib{lib_name}.so*'<br>            src_lib_pattern = os.path.join(dst_path, lib_file_pattern)<br>            matching_files = glob.glob(src_lib_pattern)<br><br>            if not matching_files:<br>                # 如果找不到 .so 文件,尝试调用 addsymlink.sh 更新软链接<br>                addsymlink_script = os.path.join(dst_path, 'addsymlink.sh')<br>                if os.path.exists(addsymlink_script):<br>                    print(f"Library {lib_file_pattern} not found. Running addsymlink.sh to update symlinks...")<br>                    try:<br>                        subprocess.check_call([addsymlink_script], cwd=dst_path)<br>                    except subprocess.CalledProcessError as e:<br>                        print(f"Error: Failed to run addsymlink.sh: {e}")<br>                        sys.exit(1)<br><br>                    # 再次查找 .so 文件<br>                    matching_files = glob.glob(src_lib_pattern)<br>                    if not matching_files:<br>                        print(f"Error: Library {lib_file_pattern} still not found after running addsymlink.sh.")<br>                        sys.exit(1)<br>                else:<br>                    print(f"Error: Library {lib_file_pattern} not found and addsymlink.sh does not exist.")<br>                    sys.exit(1)<br><br>            # 拷贝所有匹配的 .so* 文件<br>            for src_lib_path in matching_files:<br>                dest_lib_path = os.path.join(src_path, os.path.basename(src_lib_path))<br>                shutil.copy2(src_lib_path, dest_lib_path)<br>                print(f"Copied {src_lib_path} to {dest_lib_path}")<br><br>        else:<br>            print(f"Unsupported platform: {PLATFORM}")<br>            sys.exit(1)
复制代码
2.看了下prepare_depends_libs函数有这么一段,可以指定把对应文件夹给拷贝到BUILD_DIR下:
  1. if 'needed_files' in lib:<br>            if Script.GetOption('clean'):<br>                clear_needed_files(helper, lib['root'], lib['needed_files'])<br>            else:<br>                copy_needed_files(helper, lib['root'], lib['needed_files'])
复制代码
那么SConstruct可以改成这样:
  1. DEPENDS_LIBS += [<br>    {<br>        'root': BINARY_LIB_PATHS,<br>        'shared_libs':[],<br>        'needed_files': ['bin']<br>    }<br><br>]  <br>app.prepare_depends_libs(ARGUMENTS, helper, DEPENDS_LIBS)
复制代码
效果一致,后者好处是不用写新函数(虽然这种逻辑AI编写成本挺低)
上面两个方法适用于三方库都在同一个文件夹的情况。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册