三方库组织

公司的项目初步三方库路径组织是这样,awtk-widget开头的是awtk的自定义控件,无源码的二进制库放在sourceless这个文件夹:

./3rd
  ├── awtk-widget-battery-widget
  ├── awtk-widget-border-text
  ├── awtk-widget-option-box
  ├── awtk-widget-range-rule-widget
  ├── awtk-widget-range-slider
  ├── awtk-widget-sonar-image
  └── sourceless

sourceless文件夹下,分不同平台版本,平台文件夹下集合所有依赖库。每个依赖库我都按一个文件夹整理,include文件夹存放头文件,bin文件夹存放so库(为什么是bin不是lib?因为awtk内置的拷贝库脚本定死了只找bin文件夹下的库)。

project/3rd/sourceless/ubuntu$ tree -L 2
.
├── cairo
│   ├── bin
│   └── include
├── freetype
│   ├── bin
│   ├── include
│   └── share
├── libjpeg-turbo
│   ├── bin
│   └── include
├── libpng
│   ├── bin
│   ├── include
│   └── share
├── openssl
│   ├── bin
│   └── include
├── pixman
│   ├── bin
│   └── include
├── poco
│   ├── bin
│   └── include
├── version.md
└── zlib
  ├── bin
  ├── include
  └── share

为什么这样放?估计当时觉得一个库就是一个package, 看着比较整洁。

这种堆放方式后面证明对项目维护十分麻烦,对于上面的cairo, poco, libjpeg, zlib等库,每一个包我都要在SConscript里面指明路径:

SOURCE_3RD_DEPS_PATH = os.path.normpath(os.path.join(os.getcwd(), '3rd/sourceless'))

if PLATFORM == 'Linux':
   if LINUX_FB == True:
       SOURCE_3RD_DEPS_PATH = (os.path.join(SOURCE_3RD_DEPS_PATH, 'T113'))
   else:
        SOURCE_3RD_DEPS_PATH = (os.path.join(SOURCE_3RD_DEPS_PATH, 'ubuntu'))
elif PLATFORM == 'Windows':
   SOURCE_3RD_DEPS_PATH = os.path.join(SOURCE_3RD_DEPS_PATH, 'win')
   
DEPENDS_LIBS += [
  {
           'root' : os.path.join(SOURCE_3RD_DEPS_PATH, 'cairo'),
           'shared_libs' : ['cairo']
      },
      {
           'root' : os.path.join(SOURCE_3RD_DEPS_PATH, 'freetype'),
           'shared_libs' : ['freetype']
      },
      {
           'root' : os.path.join(SOURCE_3RD_DEPS_PATH, 'libjpeg-turbo'),
           'shared_libs' : ['jpeg', 'turbojpeg']
      },
      {
           'root' : os.path.join(SOURCE_3RD_DEPS_PATH, 'pixman'),
           'shared_libs': ['pixman-1']
      },
      {
           'root' : os.path.join(SOURCE_3RD_DEPS_PATH, 'libpng'),
           'shared_libs' : ['png16']
      },
      {
           'root' : os.path.join(SOURCE_3RD_DEPS_PATH, 'zlib'),
           'shared_libs' : ['z']
      },
      {
           'root': os.path.join(SOURCE_3RD_DEPS_PATH, 'openssl'),
           'shared_libs': ['crypto', 'ssl']
      },
      {
           'root' : os.path.join(SOURCE_3RD_DEPS_PATH, 'poco'),
           'shared_libs' : ['PocoFoundation', 'PocoUtil', 'PocoXML', 'PocoJSON', 'PocoCrypto', 'PocoNet', 'PocoNetSSL']
      }
]
...

helper.set_deps(DEPENDS_LIBS)

这样每加一个库我都要更新一次DEPEND_LIBS,几个库的小项目还好说,要是几十个库还得了。

awtk的helper.set_deps()函数可以自动把shared_libs指定的库列表拷贝过去,但一些情景也有失灵的时候,只能手动拷贝,这种结构一个个文件夹去拷贝肯定特别酸爽。

后面我参考linux内对头文件和so文件的组织方法,把头文件和库文件夹都扁平化了:

project/3rd/binary/ubuntu$ tree -L 2
.
├── bin
│   ├── addsymlinks.sh
│   ├── libcairo.so -> libcairo.so.2
│   ├── libcairo.so.2 -> libcairo.so.2.11600
│   ├── libcairo.so.2.11600 -> libcairo.so.2.11600.0
│   ├── libcairo.so.2.11600.0
│   ├── libcrypto.so -> libcrypto.so.1
│   ├── libcrypto.so.1 -> libcrypto.so.1.1
│   ├── libcrypto.so.1.1
│   ├── libfreetype.so -> libfreetype.so.6
│   ├── libfreetype.so.6 -> libfreetype.so.6.20
│   ├── libfreetype.so.6.20 -> libfreetype.so.6.20.0
│   ├── libfreetype.so.6.20.0
│   ├── libjpeg.so -> libjpeg.so.62
│   ├── libjpeg.so.62 -> libjpeg.so.62.4
│   ├── libjpeg.so.62.4 -> libjpeg.so.62.4.0
│   ├── libjpeg.so.62.4.0
....
├── include
│   ├── cairo
│   ├── cJSON.h
│   ├── evconfig-private.h
│   ├── event2
│   ├── freetype2
│   ├── jconfig.h
│   ├── jerror.h
│   ├── jmorecfg.h
│   ├── jpeglib.h
│   ├── libpng16
│   ├── mosquitto_broker.h
│   ├── mosquitto.h
....
└── VERSION.md

这样后面加三方库只需要CV include和bin就行了,不用修改SConstruct, 要手动拷贝直接cp -P xxx/bin/* *就行。

THIRD_SRC = os.path.join(os.getcwd(), '3rd/')
BINARY_SRC = os.path.join(THIRD_SRC, 'binary')
if PLATFORM == 'Linux':
  if LINUX_FB == True:
      BINARY_SRC = (os.path.join(BINARY_SRC, 't113'))
  else:
      BINARY_SRC = (os.path.join(BINARY_SRC, 'ubuntu'))

BINARY_LIB_PATH = [
  os.path.join(BINARY_SRC, 'bin')
]

BINARY_LIB_NAMES = ["mosquitto", "mosquittopp", "nanomsg"]
BINARY_LIB_NAMES += ["cairo", "freetype", "jpeg", "turbojpeg", "pixman-1", "png16", "z", "crypto", "ssl"]
BINARY_LIB_NAMES += ["PocoFoundation", "PocoUtil", "PocoXML", "PocoJSON", "PocoCrypto", "PocoNet", "PocoNetSSL"]


DEPENDS_LIBS += [
  {
      "root" : os.path.join(BINARY_LIB_PATH, ''),
      'shared_libs': BINARY_LIB_NAMES,
      'static_libs': []
  }
]  

项目连带自定义控件一起编译

需要加这么一行,不然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):
       if not self.BUILD_DIR:
           Script.SConscript(SConscriptFiles)
       else:
           env = Environment.Environment()
           env.Default(self.BUILD_DIR)
           for sc in SConscriptFiles:
               dir = os.path.dirname(sc)
               build_dir = os.path.join(self.BUILD_DIR, dir)
               Script.SConscript(sc, variant_dir=build_dir, duplicate=False)

其中self.BUILD_DIR是awtk可通过ARGUMENTS指定的环境参数。

在项目的SConstrct里可以这么加,我的情况是需要跨windows, ubuntu, 嵌入式linux机器三个平台:

PLATFORM = platform.system()
if PLATFORM == 'Linux':
   if LINUX_FB == True:
       ARGUMENTS['BUILD_DIR'] = 'build_linux_fb'
   else:
       ARGUMENTS['BUILD_DIR'] = 'build_ubuntu'
elif PLATFORM == 'Windows':
   ARGUMENTS['BUILD_DIR'] = 'build_windows'

测试环境为ubuntu20.04, 现在在项目根目录分别执行sconsscons LINUX_FB=true, 就能看到两个平台的输出分别被放到build_ubuntubuild_linux_fb文件夹了。

├── build_linux_fb
│ ├── bin
│ ├── lib
│ ├── src
│ └── tests
├── build_ubuntu
│ ├── bin
│ ├── lib
│ ├── src
│ └── tests

不过也有副作用,如果加了三方库的话,对于之前用helper.set_deps(DEPENDS_LIBS) , DEPEND_LIBS里指定的每个三方库文件夹三方库必须被放在BUILD_DIR下,也就是我现在对于之前的二进制库不得不改成这样:

project/3rd/binary$ tree -L 2
.
├── build_linux_fb
│ ├── bin
│ ├── include
│ └── VERSION.md
└── build_ubuntu
├── bin
├── include
└── VERSION.md

同时项目的scripts/release.py需要修改输出路径,默认是写死bin的。

OS_NAME = platform.system()
PRJ_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
OUTPUT_DIR = join_path(PRJ_DIR, 'release')
# old
# BIN_DIR = join_path(PRJ_DIR, 'bin')
# new
BIN_DIR = join_path(PRJ_DIR, 'build_linux_fb/bin')

三方库软链接问题

项目用到mosquitto和poco两个库,编译的时候发现bin/ld报找不到库:

/bin/ld: cannot find -lmosquitto
/bin/ld: cannot find -lmosquittopp
/bin/ld: cannot find -lnanomsg
/bin/ld: cannot find -lPocoFoundation
/bin/ld: cannot find -lPocoUtil
/bin/ld: cannot find -lPocoXML
/bin/ld: cannot find -lPocoJSON
/bin/ld: cannot find -lPocoCrypto
/bin/ld: cannot find -lPocoNet
/bin/ld: cannot find -lPocoNetSSL

工作由于赶工,一时半会找不到办法,只好把用到的库都拷贝到/usr/local/lib去,但毕竟会污染环境,每次部署到一个新环境就得这么做一次,不是什么好方法。

有时间后开始探索,检查编译log,明明-L路径已经指明到有库的路径3rd/binary/build_ubuntu/bin

scons: Building targets ...
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
中间略去一堆....
==================================================
ld: mode elf_x86_64
attempt to open /usr/local/lib/x86_64-linux-gnu/libPocoFoundation.so failed
attempt to open /usr/local/lib/x86_64-linux-gnu/libPocoFoundation.a failed
attempt to open /lib/x86_64-linux-gnu/libPocoFoundation.so failed
attempt to open /lib/x86_64-linux-gnu/libPocoFoundation.a failed
attempt to open /usr/lib/x86_64-linux-gnu/libPocoFoundation.so failed
attempt to open /usr/lib/x86_64-linux-gnu/libPocoFoundation.a failed
attempt to open /usr/lib/x86_64-linux-gnu64/libPocoFoundation.so failed
attempt to open /usr/lib/x86_64-linux-gnu64/libPocoFoundation.a failed
attempt to open /usr/local/lib64/libPocoFoundation.so failed
attempt to open /usr/local/lib64/libPocoFoundation.a failed
attempt to open /lib64/libPocoFoundation.so failed
attempt to open /lib64/libPocoFoundation.a failed
attempt to open /usr/lib64/libPocoFoundation.so failed
attempt to open /usr/lib64/libPocoFoundation.a failed
attempt to open /usr/local/lib/libPocoFoundation.so failed
attempt to open /usr/local/lib/libPocoFoundation.a failed
attempt to open /lib/libPocoFoundation.so failed
attempt to open /lib/libPocoFoundation.a failed
attempt to open /usr/lib/libPocoFoundation.so failed
attempt to open /usr/lib/libPocoFoundation.a failed
attempt to open /usr/x86_64-linux-gnu/lib64/libPocoFoundation.so failed
attempt to open /usr/x86_64-linux-gnu/lib64/libPocoFoundation.a failed
attempt to open /usr/x86_64-linux-gnu/lib/libPocoFoundation.so failed
attempt to open /usr/x86_64-linux-gnu/lib/libPocoFoundation.a failed

原来定死了是从无后缀的.so开始找的,无后缀so通常都是指向带后缀版本so库的软链接,而我的lib路径下都是带版本后缀的,反而没有软链接,估计是当时源码编译后直接CV手动拷贝,于是那些软链接就丢失了,后面才查到应该用cp -P去拷贝。

一个个ln -s去给这些so库建链接肯定不是什么好办法,遂在网上寻找可以自动建链接的脚本,果然找到一个, 自己学习过程加工了下,如下:

#!/bin/bash
# liblinks - generate symbolic links
# given libx.so.0.0.0 this would generate links for libx.so.0.0, libx.so.0, libx.so
#
# ref: https://stackoverflow.com/questions/462100/bash-script-to-create-symbolic-links-to-shared-libraries
LIBFILES=`ls lib*.so.*`
for FILE in $LIBFILES;
do
shortlib=$FILE
basename=$FILE
echo "=================In loop========================"
while extn=$(echo $shortlib | sed -n '/\.[0-9][0-9]*$/s/.*\(\.[0-9][0-9]*\)$/\1/p')
echo "basename: $basename extn:$extn"

[ -n "$extn" ]

do
shortlib=$(basename $shortlib $extn)
echo "ln -fs $basename $shortlib"
ln -fs $basename $shortlib
basename=$shortlib
done
echo "=====================Out loop=================="
done

将脚本命名为addsymlink.sh,放到三方库bin文件夹中,执行,然后回到项目根目录scons编译,就没再报bin/ld的问题了,看来就是无软链接所致,当前在windows上只知道dll不知道lib的教训在linux上又演了一把。

不过这个方法并没有完美解决所有问题,有些库十分顽固,必须提供带版本号的版本,否则就无法运行:

kp25s_expo$ ./bin/demo 
./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 
linux-vdso.so.1 (0x00007fff637d0000)
libPocoFoundation.so.64 => /home/zhangdalin/AWStudioProjects/project/3rd/sourceless/ubuntu/poco/bin/libPocoFoundation.so.64 (0x00007f15a20d5000)
libPocoNet.so.64 => /home/zhangdalin/AWStudioProjects/project/3rd/sourceless/ubuntu/poco/bin/libPocoNet.so.64 (0x00007f15a1f94000)
libPocoNetSSL.so.64 => /home/zhangdalin/AWStudioProjects/project/3rd/sourceless/ubuntu/poco/bin/libPocoNetSSL.so.64 (0x00007f15a1f47000)
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
import shutil
import subprocess
import sys
import glob
import platform

def copy_binary_libs_to_build_dir(dst_path, lib_list, src_path):
"""
将 dst_path 中匹配 lib_list 的文件拷贝到 src_path 文件夹。
对于 Linux 系统,会查找 lib_list 中对应元素的 .so 文件是否存在,
如果不存在就调用 dst_path 中的 addsymlink.sh 更新软链接。
Linux 系统情况下,将拷贝对应元素所有的匹配 .so* 的文件(包括软链接和带版本号后缀的 so 文件)。
"""
PLATFORM = platform.system()
# 确保目标目录存在
os.makedirs(src_path, exist_ok=True)

for lib_name in lib_list:
# 根据平台确定库文件的后缀
if PLATFORM == 'Windows':
lib_file = f'{lib_name}.dll'
src_lib_path = os.path.join(dst_path, lib_file)
dest_lib_path = os.path.join(src_path, lib_file)

# 检查库文件是否存在
if not os.path.exists(src_lib_path):
print(f"Warning: Library {lib_file} not found in {dst_path}.")
continue

# 拷贝文件
shutil.copy2(src_lib_path, dest_lib_path)
print(f"Copied {src_lib_path} to {dest_lib_path}.")

elif PLATFORM == 'Linux':
# Linux 系统下,查找 lib_name 对应的 .so 文件
lib_file_pattern = f'lib{lib_name}.so*'
src_lib_pattern = os.path.join(dst_path, lib_file_pattern)
matching_files = glob.glob(src_lib_pattern)

if not matching_files:
# 如果找不到 .so 文件,尝试调用 addsymlink.sh 更新软链接
addsymlink_script = os.path.join(dst_path, 'addsymlink.sh')
if os.path.exists(addsymlink_script):
print(f"Library {lib_file_pattern} not found. Running addsymlink.sh to update symlinks...")
try:
subprocess.check_call([addsymlink_script], cwd=dst_path)
except subprocess.CalledProcessError as e:
print(f"Error: Failed to run addsymlink.sh: {e}")
sys.exit(1)

# 再次查找 .so 文件
matching_files = glob.glob(src_lib_pattern)
if not matching_files:
print(f"Error: Library {lib_file_pattern} still not found after running addsymlink.sh.")
sys.exit(1)
else:
print(f"Error: Library {lib_file_pattern} not found and addsymlink.sh does not exist.")
sys.exit(1)

# 拷贝所有匹配的 .so* 文件
for src_lib_path in matching_files:
dest_lib_path = os.path.join(src_path, os.path.basename(src_lib_path))
shutil.copy2(src_lib_path, dest_lib_path)
print(f"Copied {src_lib_path} to {dest_lib_path}")

else:
print(f"Unsupported platform: {PLATFORM}")
sys.exit(1)

2.看了下prepare_depends_libs函数有这么一段,可以指定把对应文件夹给拷贝到BUILD_DIR下:

 if 'needed_files' in lib:
if Script.GetOption('clean'):
clear_needed_files(helper, lib['root'], lib['needed_files'])
else:
copy_needed_files(helper, lib['root'], lib['needed_files'])

那么SConstruct可以改成这样:

DEPENDS_LIBS += [
{
'root': BINARY_LIB_PATHS,
'shared_libs':[],
'needed_files': ['bin']
}

]
app.prepare_depends_libs(ARGUMENTS, helper, DEPENDS_LIBS)

效果一致,后者好处是不用写新函数(虽然这种逻辑AI编写成本挺低)

上面两个方法适用于三方库都在同一个文件夹的情况。

AWTK项目编译问题整理(1)的更多相关文章

  1. 用C写一个web服务器(三) Linux下用GCC进行项目编译

    .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...

  2. Xamarin.iOS项目编译提示Could not AOT the assembly

    Xamarin.iOS项目编译提示Could not AOT the assembly 错误信息:Could not AOT the assembly **************.dll 这个错误是 ...

  3. 使用maven编译dubbo,导入eclipse(其他maven开源项目编译类似)

    dubbo github下载地址:https://github.com/alibaba/dubbo 相关文档:http://dubbo.io/ 使用maven编译dubbo,导入eclipse(其他m ...

  4. .NET 4 新建 webform 项目编译不通过

    .NET 4 新建 webform 项目编译不通过,可能需要做如下改动. 一.根据编译错误,删除 packages.config 中相关包 二.安装包install-package Microsoft ...

  5. Asp.Net MVC安全更新MS14-059导致项目编译失败

    微软最近一次安全更新MS14-059(链接:https://technet.microsoft.com/en-us/library/security/ms14-059)由于直接应用到了machine. ...

  6. thinkphp学习笔记3—项目编译和调试模式

    原文:thinkphp学习笔记3-项目编译和调试模式 1.项目编译 在章节2.4项目编译中作者讲到使用thinkphp的项目在第一次运行的时候会吧核心需要加载的文件去掉空白和注释合并到一个文件中编译并 ...

  7. Jenkins结合.net平台之Web项目编译

    前面我们讲解了如何使用msbuild.exe编译一个.net程序.示例中我们讲解的是编译控制台项目,但是我们知道web项目不仅需要编译类的嵌入的资源文件,还要拷贝诸如css,html,js,图片等资源 ...

  8. Tomcat 多项目部署方法整理

    Tomcat 多项目部署方法整理 说明:tomcat-deploy-aaa和tomcat-deploy-bbb是两个不同的web项目,为了方便以下简称aaa和bbb,请先自行创建并跑通 导航: NO1 ...

  9. idea使用docker-maven-plugin插件将项目编译为docker镜像到远程linux服务器 原

    在使用idea开发时,直接docker-maven-plugin插件,把项目编译成docker镜像,然后通过docker:push推送到linux服务器,非常简单,快捷,也避免了手动打包,然后拷贝wa ...

  10. eclipse指定项目编译级别

    指定项目编译级别Eclipse→Preferences→Java→Compiler→Compiler compliance level:1.6或其他 或者,

随机推荐

  1. 《机器人SLAM导航核心技术与实战》第1季:第7章_SLAM中的数学基础

    <机器人SLAM导航核心技术与实战>第1季:第7章_SLAM中的数学基础 视频讲解 [第1季]7.第7章_SLAM中的数学基础-视频讲解 [第1季]7.1.第7章_SLAM中的数学基础_S ...

  2. SearXNG+MCP实现搜索引擎,想怎么搜就怎么搜

    一.概述 MCP应用市场,有很多搜索引擎的应用.但是你们会发现,普遍都需要api-key.你必须花钱购买api-key才能实现搜索功能. 问题来了,我就想用免费搜索的,就向百度一样,可不可以? 答案是 ...

  3. 『Plotly实战指南』--布局进阶篇

    在数据可视化领域,Plotly的子图布局是打造专业级仪表盘的核心武器. 当面对多维数据集时,合理的子图布局能显著提升多数据关联分析效率,让数据的呈现更加直观和美观. 本文将深入探讨Plotly中子图布 ...

  4. Web客户端开发

    Web开发工具 从高层次来看,可以将客户端工具放入以下三大类需要解决的问题中: 安全网络 - 在代码开发期间有用的工具. 转换 - 以某种方式转换代码的工具,例如将一种中间语言转换为浏览器可以理解的 ...

  5. 2.7K star!这个汉字工具库让中文处理变得超简单,开发者必备!

    嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 cnchar 是一个功能全面的汉字工具库,提供拼音转换.笔画动画.偏旁查询.成语接龙.语音合 ...

  6. 基于SaaS纯BS架构的全院级PACS系统

           2014年曾经做过一版简单的Dicom Web Viewer,之前的Web版本由于技术和功能的极限性,仅能简单的运用于临床阅片和患者的电子胶片使用,无法普及到放射和超声等影像科室.影像科 ...

  7. 代码随想录第二天 | Leecode 209. 长度最小的子数组、59. 螺旋矩阵II

    Leecode 209 长度最小的子数组 题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/ 题目描述 给定一个含有 n 个正整数 ...

  8. 使用C#构建一个同时问多个LLM并总结的小工具

    前言 在AI编程时代,如果自己能够知道一些可行的解决方案,那么描述清楚交给AI,可以有很大的帮助. 但是我们往往不知道真正可行的解决方案是什么? 我自己有过这样的经历,遇到一个需求,我不知道有哪些解决 ...

  9. 操作系统综合题之“采用短进程优先调度算法(Shortest-Process-First,SPF)和先来先服务调度算法(First-Come,First-Served,FCFS)计算开始运行时间、结束时间、等待时间、周转时间、带权周转时间、平均周转时间”

    一.问题:某系统中有四个进程,他们进入系统的时间和需要服务的时间如题下表所示(表中数值均为十进制) 进程 进入系统的时间 需要服务的时间 P1 0 100 P2 10 60 P3 25 25 P4 3 ...

  10. ASP.NET Core中DI中Add*方法对类的假定

    在ASP.NET Core的依赖注入(DI)容器中,当你使用Add*方法和泛型类型来指定要注册的类时,容器会做出以下假设: (1)类必须是具体类(Concrete Class):使用Add*方法注册的 ...