三方库组织
公司的项目初步三方库路径组织是这样,awtk-widget开头的是awtk的自定义控件,无源码的二进制库放在sourceless这个文件夹:- ./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文件夹下的库)。- 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里面指明路径:- 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文件的组织方法,把头文件和库文件夹都扁平化了:- 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/* *就行。- 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只会编译项目本身:- 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看到这么一段:- 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机器三个平台:- 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文件夹了。- ├── 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下,也就是我现在对于之前的二进制库不得不改成这样:- 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的。- 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报找不到库:- /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:- 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可以输出链接库的查找过程,于是我试了下输出:- 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库建链接肯定不是什么好办法,遂在网上寻找可以自动建链接的脚本,果然找到一个, 自己学习过程加工了下,如下:- #!/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上又演了一把。
不过这个方法并没有完美解决所有问题,有些库十分顽固,必须提供带版本号的版本,否则就无法运行:- 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库就没有,不知道是我设了什么环境变量导致还是库的特性。- 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.自己写拷贝函数,指定文件夹把库拷过去:- 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下:- 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可以改成这样:- 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编写成本挺低)
上面两个方法适用于三方库都在同一个文件夹的情况。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |