当前位置: 首页 > news >正文

Cmake 使用教程

介绍

CMake 是一个开源、跨平台的构建系统,主要用于软件的构建、测试和打包。CMake 使用平台无关的配置文件 CMakeLists.txt 来控制软件的编译过程,并生成适用于不同编译器环境的项目文件。例如,它可以生成 Unix 系统的 Makefile Windows 下 的 Visual Studio 项目文件或 Mac Xcode 工程文件,从而简化了跨平台和交叉编译的工作流程。CMake 并不直接构建软件,而是产生标准的构建文件,然后使用这些文件在各自的构建环境中构建软件。
CMake 有以下几个特点:
  • 开放源代码:使⽤类 BSD 许可发布
  • 跨平台:并可⽣成编译配置⽂件,在 Linux/Unix 平台,⽣成 makefile;在苹果平台,可以生成 xcode;在 Windows 平台,可以⽣成 MSVC 的工程文件
  • 能够管理⼤型项⽬:KDE4 就是最好的证明
  • 简化编译构建过程和编译过程:Cmake 的⼯具链⾮常简单:cmake+make
  • ⾼效率:按照 KDE 官⽅说法,CMake 构建 KDE4 kdelibs 要⽐使⽤autotools来构建 KDE3.5.6 kdelibs 40%,主要是因为 Cmake 在⼯具链中没有 libtool
  • 可扩展:可以为 cmake 编写特定功能的模块,扩充 cmake 功能

安装

Ubuntu 下安装 cmake

sudo apt update
sudo apt install cmake
确定 cmake 是否安装成功
cmake --version

入门样例 - Hello-world 工程

创建 hello 目录, 并在其目录下创建 main.cpp 源文件和 CMakeLists.txt 文件。
main.cpp文件内容如下
#include <iostream>
using namespace std;
int main()
{std::cout << "hello world" << std::endl;return 0;
}
CMakeLists.txt 文件内容如下
# 声明所需的cmake版本
cmake_minimum_required(VERSION 3.0)
# 定义项目工程名称
project(hello)
# 设置生成目标
add_executable(main main.cpp)
  • cmake_minimum_required:指定使用的 cmake 的最低版本。可选,如果不加会有警告。
  • project:定义工程名称, 并可指定工程的版本、工程描述、web 主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
  • add_executable:定义工程会生成一个可执行程序,这里的可执行程序名和 project 中的项目名没有任何关系,源文件名可以是一个也可以是多个,如有多个可用空格或;间隔
# 样式 1 
add_executable(app test1.c test2.c test3.c)
# 样式 2 
add_executable(app test1.c;test2.c;test3.c)
此时我们可以使用 cmake 来构建这个工程,生成 makefile, 从而编译代码。
cmake .
make

我们可以看到如果在 CMakeLists.txt 文件所在目录执行了 cmake 命令之后就会生成一些目录和文件,如果再基于 makefile 文件执行 make 命令,程序在编译过程中还会生成一些中间文件和一个可执行文件,这样会导致整个项目目录看起来很混乱,不太容易管理和维护。

这其实被称为内部构建,但 CMake 强烈推荐的做法是外部构建。此时我们可以把生成的这些与项目源码无关的文件统一放到一个对应的目录里边,比如将这个目录命名为 build,这就叫做外部构建。

mkdir build
cd build
cmake ..
make

使用

定义变量

假如我们的项目中存在多个源文件,并且这些源文件需要被反复使用,每次都直接将它们的名字写出来确实是很麻烦,此时我们就可以定义一个变量,将文件名对应的字符串存储起来,在 cmake 里定义变量需要使用 set 指令。
# [] 中的参数为可选项, 如不需要可以不写,VAR 表示变量名, VALUE 表示变量值。
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
样例
set(SRC_LIST test1.c test2.c test3.c)
add_executable(app ${SRC_LIST})
注意:变量使⽤ ${} ⽅式取值,但是在 if 控制语句中是直接使⽤变量名

预定义变量

预定义变量名称备注
CMAKE_CXX_STANDARD
c++特性标准
CMAKE_CURRENT_BINARY_DIR
cmake 执行命令时所在的工作路径
CMAKE_CURRENT_SOURCE_DIR
CMakeLists.txt 所在目录
CMAKE_INSTALL_PREFIX
默认安装路径
样例
#增加-std=c++14
set(CMAKE_CXX_STANDARD 14)

包含头文件

在编译项目源文件的时候,很多时候都需要将源文件对应的头文件路径指定出来,这样才能保证在编译过程中编译器能够找到这些头文件,并顺利通过编译。在 CMake 中设置头文件路径也很简单,通过命令 include_directories 就可以搞定了。
include_directories(headpath)

添加生成目标

add_executable(target srcfiles1 srcfile2 ...)

链接动态库/静态库

在编写程序的过程中,可能会用到一些系统提供的动态库或者自己制作出的动态库或者静态库文件,cmake 中也为我们提供了相关的加载静态库 / 动态库的命令。

链接静态库

cmake 中,链接静态库的命令如下:
link_libraries(<static lib> [<static lib>...])
参数为指定要链接的静态库的名字,可以是全名, 也可以是去掉 lib .a 之后的名字。如果该静态库是自己制作或者使用第三方提供的静态库,可能出现静态库找不到的情况,此时需要将静态库的路径也指定出来:
link_directories(<lib path>)

链接动态库

cmake 中链接动态库的命令如下 :
target_link_libraries(<target><PRIVATE|PUBLIC|INTERFACE> <item>...[<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
  • target:指定要加载动态库文件的名字
  • PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为 PUBLIC。如果各个动态库之间没有依赖关系,无需做任何设置,三者没有没有区别,一般无需指定,使用默认的 PUBLIC 即可
  1. PUBLIC:在 public 后面的库会被 Link 到前面的 target 中,并且里面的符号也会被导出,提供给第三方使用
  2. PRIVATE:在 private 后面的库仅被 link 到前面的 target 中,并且终结掉,第三方不能感知你调了啥库
  3. INTERFACE:在 interface 后面引入的库不会被链接到前面的 target 中,只会导出符号
动态库的链接和静态库是完全不同的:
  • 静态库会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了。
  • 动态库在生成可执行程序的链接阶段不会被打包到可执行程序中,当可执行程序被启动并且调用了动态库中的函数的时候,动态库才会被加载到内存。
因此,在 cmake 中指定要链接的动态库的时候,应该将命令写到生成了可执行文件之后:
cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 添加并指定最终生成的可执行程序名
add_executable(app ${SRC_LIST})
# 指定可执行程序要链接的动态库名字
target_link_libraries(app pthread)
target_link_libraries(app pthread) 中: app 表示最终生成的可执行程序的名字;pthread 表示可执行程序要加载的动态库, 全名为 libpthread.so , 在指定的时候一般会掐头(lib )去尾( .so )。
有些时候,当我们去链接第三方的动态库的时候, 如果不指定链接路径,会报错找不到动态库。此时,我们在生成可执行程序之前,通过命令指定出要链接的动态库的位置:
link_directories(path)
通过 link_directories 指定了动态库的路径之后,在执行生成的可执行程序的时候,就不会出现找不到动态库的问题了。

搜索文件

如果一个项目里边的源文件很多,在编写 CMakeLists.txt 文件的时候不可能将项目目录的各个文件一一罗列出来,这样太麻烦了。所以在 CMake 中为我们提供了搜索文件的命令:

aux_source_directory

aux_source_directory(< dir > < variable >)
  • dir 表示要搜索的目录
  • variable 表示将从 dir 目录下搜索到的源文件列表存储到该变量中

file

file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
  • GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中
  • 递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中
样例
file(GLOB SRC_LISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
file(GLOB HEAD_LISTS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
CMAKE_CURRENT_SOURCE_DIR 宏表示当前访问的 CMakeLists.txt 文件所在的路径

message 指令

CMake 中可以使用命令打印消息,该命令的名字为 message
message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
第一个参数通常不设置, 表示重要消息。
  • STATUS :非重要消息
  • WARNINGCMake 警告, 会继续执行
  • AUTHOR_WARNINGCMake 警告 (dev), 会继续执行
  • SEND_ERRORCMake 错误, 继续执行,但是会跳过生成的步骤
  • FATAL_ERRORCMake 错误, 终止所有处理过程
# 输出一般日志信息
message(STATUS "source path: ${PROJECT_SOURCE_DIR}")
# 输出警告信息
message(WARNING "source path: ${PROJECT_SOURCE_DIR}")
# 输出错误信息
message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}")

判断文件是否存在

if (NOT EXISTS file)
endif()

循环遍历

foreach(val vals)
endforeach()

执行外部指令

add_custom_command(PRE_BUILD 表示在所有其他步骤之前执行自定义命令COMMAND 要执行的指令名称ARGS 要执行的指令运行参数选项DEPENDS 指定命令的依赖项OUTPUT 指定要生成的目标名称COMMENT 执行命令时要打印的内容
)

install 指令

install 指令⽤于定义安装规则,安装的内容可以包括⽬标⼆进制、动态库、静态库以及⽂件、⽬录、脚本等。
INSTALL(TARGETS targets... [[ARCHIVE|LIBRARY|RUNTIME] [DESTINATION][PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT ] [OPTIONAL] ] [...])
  • 参数中的 TARGETS 后⾯跟的就是我们通过 ADD_EXECUTABLE 或者 ADD_LIBRARY 定义的⽬标⽂件,可能是可执⾏⼆进制、动态库、静态库。
  • ⽬标类型也就相对应的有三种,ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME 特指可执⾏⽬标⼆进制。
  • DESTINATION 定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候 CMAKE_INSTALL_PREFIX 其实就⽆效了。如果你希望使⽤ CMAKE_INSTALL_PREFIX 来定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是 ${CMAKE_INSTALL_PREFIX}/...
例子:
INSTALL(TARGETS myrun mylib mystaticlib RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION libstatic)
上⾯的例⼦会将:
  • 可执⾏⼆进制 myrun 安装到${CMAKE_INSTALL_PREFIX}/bin 目录
  • 动态库 libmylib 安装到${CMAKE_INSTALL_PREFIX}/lib 目录
  • 静态库 libmystaticlib 安装到${CMAKE_INSTALL_PREFIX}/libstatic 目录

嵌套的 CMake

如果项目很大,或者项目中有很多的源码目录,在通过 CMake 管理项目的时候如果只使用一个 CMakeLists.txt ,那么这个文件相对会比较复杂,有一种化繁为简的方式就是给每个源码目录都添加一个 CMakeLists.txt 文件(头文件目录不需要),这样每个文件都不会太复杂,而且更灵活,更容易维护。
嵌套的 CMake 是一个树状结构,最顶层的 CMakeLists.txt 是根节点,其次都是子节点。因此,我们需要了解一些关于 CMakeLists.txt 文件变量作用域的一些信息:
  • 根节点 CMakeLists.txt 中的变量全局有效
  • 父节点 CMakeLists.txt 中的变量可以在子节点中使用
  • 子节点 CMakeLists.txt 中的变量只能在当前节点中使用
我们还需要知道在 CMake 中父子节点之间的关系是如何建立的,这里需要用到一个 CMake 命令:
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
  • source_dir:指定了 CMakeLists.txt 源文件和代码文件的位置,其实就是指定子目录
  • binary_dir:指定了输出文件的路径,一般不需要指定,忽略即可。
  • EXCLUDE_FROM_ALL:在子路径下的目标默认不会被包含到父路径的 ALL 目标里,并且也会被排除在 IDE 工程文件之外。用户必须显式构建在子路径下的目标。
通过这种方式 CMakeLists.txt 文件之间的父子关系就被构建出来了。

案例

我们以微服务架构为例,在一个 test_odb 目录下为综合的大项目,创建一个根 CMakeLists.txt 文件,其下有一个 test 目录,为大项目中的一个小服务,目录结构如下:

根 CMakeLists.txt 文件如下

# 1. 添加cmake版本说明
cmake_minimum_required(VERSION 3.1)
# 2. 声明工程名称
project(all-test)
# 3. 添加子目录
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/test)
# 4. 设置安装目录
set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR})

在 test 目录结构如下,其有一个子节点 CMakeLists.txt 文件,有 entity 目录存放odb映射文件,source 目录存放源文件。

student.hxx 文件如下

#pragma once
#include <string>
#include <cstddef> // std::size_t
#include <odb/nullable.hxx>
#include <odb/core.hxx>
#include <iostream>#pragma db object
class Student
{
public:Student() {}Student(unsigned long sn, const std::string &name, unsigned short age, unsigned long cid) : _sn(sn), _name(name), _age(age), _classes_id(cid) {}void sn(unsigned long num) { _sn = num; }unsigned long sn() { return _sn; }void name(const std::string &name) { _name = name; }std::string name() { return _name; }void age(unsigned short num) { _age = num; }odb::nullable<unsigned short> age() { return _age; }void classes_id(unsigned long cid) { _classes_id = cid; }unsigned long classes_id() { return _classes_id; }private:// 将 odb::access 类作为 person 类的朋友。// 这是使数据库支持代码可访问默认构造函数和数据成员所必需的。// 如果类具有公共默认构造函数和公共数据成员或数据成员的公共访问器和修饰符,则不需要友元声明friend class odb::access;
#pragma db id autounsigned long _id;
#pragma db uniqueunsigned long _sn;std::string _name;odb::nullable<unsigned short> _age;
#pragma db indexunsigned long _classes_id;
};#pragma db object
class Classes
{
public:Classes() {}Classes(const std::string &name) : _name(name) {}void name(const std::string &name) { _name = name; }std::string name() { return _name; }private:friend class odb::access;
#pragma db id autounsigned long _id;std::string _name;
};// 查询所有的学生信息,并显示班级名称
#pragma db view object(Student)                                      \object(Classes = classes : Student::_classes_id == classes::_id) \query((?))
struct classes_student
{
#pragma db column(Student::_id)unsigned long id;
#pragma db column(Student::_sn)unsigned long sn;
#pragma db column(Student::_name)std::string name;
#pragma db column(Student::_age)odb::nullable<unsigned short> age;
#pragma db column(classes::_name)std::string classes_name;
};// 只查询学生姓名
#pragma db view object (Student)\query((?))
struct all_name
{#pragma db column(Student::_name)std::string name;
};

main.cc 文件如下

#include <odb/database.hxx>
#include <odb/mysql/database.hxx>
#include "student.hxx"
#include "student-odb.hxx"
#include <gflags/gflags.h>DEFINE_string(host, "127.0.0.1", "这是Mysql服务器地址");
DEFINE_int32(port, 3306, "这是Mysql服务器端口");
DEFINE_string(db, "TestDB", "数据库默认库名称");
DEFINE_string(user, "root", "这是Mysql用户名");
DEFINE_string(pswd, "2162627569", "这是Mysql密码");
DEFINE_string(cset, "utf8", "这是Mysql客户端字符集");
DEFINE_int32(max_pool, 3, "这是Mysql连接池最大连接数量");void insert_classes(odb::mysql::database &db)
{try{// 获取事务对象开启事务odb::transaction trans(db.begin());Classes c1("一年级一班");Classes c2("一年级二班");db.persist(c1);db.persist(c2);// 5. 提交事务trans.commit();}catch (std::exception &e){std::cout << "插入数据出错:" << e.what() << std::endl;}
}void insert_student(odb::mysql::database &db)
{try{// 获取事务对象开启事务odb::transaction trans(db.begin());Student s1(1, "张三", 18, 1);Student s2(2, "李四", 19, 1);Student s3(3, "王五", 18, 1);Student s4(4, "赵六", 15, 2);Student s5(5, "刘七", 18, 2);Student s6(6, "孙八", 23, 2);db.persist(s1);db.persist(s2);db.persist(s3);db.persist(s4);db.persist(s5);db.persist(s6);// 5. 提交事务trans.commit();}catch (std::exception &e){std::cout << "插入学生数据出错:" << e.what() << std::endl;}
}void update_student(odb::mysql::database &db, Student &stu)
{try{// 获取事务对象开启事务odb::transaction trans(db.begin());db.update(stu);// 5. 提交事务trans.commit();}catch (std::exception &e){std::cout << "更新学生数据出错:" << e.what() << std::endl;}
}
Student select_student(odb::mysql::database &db)
{Student res;try{// 获取事务对象开启事务odb::transaction trans(db.begin());typedef odb::query<Student> query;typedef odb::result<Student> result;result r(db.query<Student>(query::name == "张三"));if (r.size() != 1){std::cout << "数据量不对!\n";return Student();}res = *r.begin();// 5. 提交事务trans.commit();}catch (std::exception &e){std::cout << "更新学生数据出错:" << e.what() << std::endl;}return res;
}void remove_student(odb::mysql::database &db)
{try{// 获取事务对象开启事务odb::transaction trans(db.begin());typedef odb::query<Student> query;db.erase_query<Student>(query::classes_id == 2);// 5. 提交事务trans.commit();}catch (std::exception &e){std::cout << "更新学生数据出错:" << e.what() << std::endl;}
}void classes_student(odb::mysql::database &db)
{try{// 获取事务对象开启事务odb::transaction trans(db.begin());typedef odb::query<struct classes_student> query;typedef odb::result<struct classes_student> result;result r(db.query<struct classes_student>(query::classes::id == 1));for (auto it = r.begin(); it != r.end(); ++it){std::cout << it->id << std::endl;std::cout << it->sn << std::endl;std::cout << it->name << std::endl;std::cout << *it->age << std::endl;std::cout << it->classes_name << std::endl;}// 5. 提交事务trans.commit();}catch (std::exception &e){std::cout << "更新学生数据出错:" << e.what() << std::endl;}
}void all_student(odb::mysql::database &db)
{try{// 获取事务对象开启事务odb::transaction trans(db.begin());typedef odb::query<Student> query;typedef odb::result<struct all_name> result;result r(db.query<struct all_name>(query::id > 0));for (auto it = r.begin(); it != r.end(); ++it){std::cout << it->name << std::endl;}// 5. 提交事务trans.commit();}catch (std::exception &e){std::cout << "查询所有学生姓名数据出错:" << e.what() << std::endl;}
}int main(int argc, char *argv[])
{google::ParseCommandLineFlags(&argc, &argv, true);// 1. 构造连接池工厂配置对象std::unique_ptr<odb::mysql::connection_pool_factory> cpf(new odb::mysql::connection_pool_factory(FLAGS_max_pool, 0));// 2. 构造数据库操作对象odb::mysql::database db(FLAGS_user, FLAGS_pswd, FLAGS_db,FLAGS_host, FLAGS_port, "", FLAGS_cset,0, std::move(cpf));// 4. 数据操作// insert_classes(db);// insert_student(db);//  auto stu = select_student(db);//  std::cout << stu.sn() << std::endl;//  std::cout << stu.name() << std::endl;//  if (stu.age()) std::cout << *stu.age() << std::endl;//  std::cout << stu.classes_id() << std::endl;// stu.age(15);// update_student(db, stu);// remove_student(db);// classes_student(db);all_student(db);return 0;
}
子节点 CMakeLists.txt 文件如下
# 1. 添加cmake版本说明
cmake_minimum_required(VERSION 3.1)
# 2. 声明工程名称
project(odb-test)# 3. 检测并生成ODB框架代码
# 3.1 添加所需的odb映射代码文件名称
set(odb_path ${CMAKE_CURRENT_SOURCE_DIR}/entity)
# 在该测试中只有一个测试代码文件
set(odb_files student.hxx)
# 3.2 检测代码文件是否生成
set(odb_hxx "")
set(odb_cxx "")
set(odb_srcs "")
foreach(odb_file ${odb_files})#3.3 如果没有生成则预定义生成指令string(REPLACE ".hxx" "-odb.hxx" odb_hxx ${odb_file})string(REPLACE ".hxx" "-odb.cxx" odb_cxx ${odb_file})if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${odb_cxx})add_custom_command(PRE_BUILD COMMAND odbARGS -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time ${odb_path}/${odb_file}DEPENDS ${odb_path}/${odb_file}OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${odb_cxx}COMMENT "生成ODB框架代码文件:" ${CMAKE_CURRENT_BINARY_DIR}/${odb_cxx})endif()# 3.4 将所有生成的框架源码文件保存起来list(APPEND odb_srcs ${CMAKE_CURRENT_BINARY_DIR}/${odb_cxx})
endforeach()# 4. 获取源码目录下的所有源码文件
set(src_files "")
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/source src_files)
# 5. 声明目标及依赖
add_executable(main ${src_files} ${odb_srcs})
# 6. 设置头文件默认搜索路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/entity)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# 7. 设置连接的库
target_link_libraries(main -lodb -lodb-mysql -lodb-boost -lgflags)
# 8. 设置安装路径
INSTALL(TARGETS main RUNTIME DESTINATION bin)

然后进入 test_odb/build 目录下,在这里构建编译

cmake ..
make

就会在该目录下生成很多目录和文件,其中可执行程序 main 就位于 test 目录下

这样编译构建生成的代码就可以与源码完美隔离开。

相关文章:

Cmake 使用教程

介绍 CMake 是一个开源、跨平台的构建系统&#xff0c;主要用于软件的构建、测试和打包。CMake 使用平台无关的配置文件 CMakeLists.txt 来控制软件的编译过程&#xff0c;并生成适用于不同编译器环境的项目文件。例如&#xff0c;它可以生成 Unix 系统的 Makefile 、 Win…...

CLIP、ViLT 与 LLaVA:多模态模型是如何看图说话的?

一、前言&#xff1a;我们已经讲了 MLLM 能力&#xff0c;但它到底是怎么实现“看图说话”的&#xff1f; 在之前文章中&#xff0c;我们已经提到了MLLM可以看图说话以及文生图等能力&#xff0c;ViT统一图文多模态架构。那模型是如何处理图文信息以及不同处理所带来的能力是怎…...

基于Springboot + vue3实现的流动摊位管理系统

项目描述 本系统包含管理员、用户、商家三个角色。 管理员角色&#xff1a; 用户管理&#xff1a;管理系统中所有用户的信息&#xff0c;包括添加、删除和修改用户。 配置管理&#xff1a;管理系统配置参数&#xff0c;如上传图片的路径等。 权限管理&#xff1a;分配和管理…...

我的软考经历

说明&#xff1a;本文分享博主软考经验&#xff0c;及软考证书在找工作时的作用。 软考 软考&#xff0c;全称&#xff1a;中国计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff0c;报名/考试/查询网址为&#xff0c;中国计算机技术职业资格网 考试分…...

C++的异常

引入&#xff1a;异常的意义是什么&#xff1f; ①&#xff1a;错误分离 将正常逻辑&#xff08;try&#xff09;与错误处理&#xff08;catch&#xff09;分离&#xff0c;避免代码被大量 if-else 污染。 ②&#xff1a;强制处理 若不捕获异常&#xff0c;程序终止&#xff0c…...

精益制造数字化转型智能工厂三年规划建设方案

该文档是精益制造数字化转型智能工厂三年规划建设方案,以打造高品质、低成本、柔性化的绿色智能工厂为愿景,围绕制造技术、自动化、数智化、管理赋能四大路径,通过夯实 EHS、品质一致性、生产安定化、现场整洁四大基石,推进标杆车间打造、联合管理、TOB 流程改善等专项。规…...

Linux 文件(3)

文章目录 1. Linux下一切皆文件2. 文件缓冲区2.1 缓冲区是什么2.2 缓冲区的刷新策略2.3 为什么要有缓冲区2.4 一个理解缓冲区刷新的例子 3. 标准错误 1. Linux下一切皆文件 在刚开始学习Linux的时候&#xff0c;我们就说Linux下一切皆文件——键盘是文件&#xff0c;显示器是文…...

Java异步编程利器:CompletableFuture 深度解析与实战

精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 一、CompletableFuture 概述 CompletableFuture是Java 8引入的异步编程工具类&#xff0c;实现了Future和CompletionStage接口&#xff0c;支持链式调用、组…...

如何支持Enhanced RTMP H.265(HEVC)

在实时音视频传输中&#xff0c;H.264长期占据主流&#xff0c;但随着视频质量要求的不断提高和带宽压力的加大&#xff0c;H.265&#xff08;HEVC&#xff09;作为下一代视频编码标准逐渐崭露头角。 在这种背景下&#xff0c;我们顺应行业发展趋势&#xff0c;成功集成了对Enh…...

Idea 查找引用jar包依赖来源的Maven pom坐标

目录 问题引入 实现解决 问题引入&#xff1a; 在查看拉取的项目&#xff0c;维护自己项目、或者迁移原有项目时&#xff0c;会遇到不知道代码中引用到的依赖从哪里引用到的。 所以利用Idea&#xff0c;从import语句到Maven项目结构树中查找&#xff0c;最终找到pom文件里的…...

Linux操作系统之进程(二):进程状态

目录 前言 一、补充知识点 1、并行与并发 2、时间片 3、 等待的本质 4、挂起 二. 进程的基本状态 三、代码演示 1、R与S 2、T 3、Z 四、孤儿进程 总结&#xff1a; 前言 在操作系统中&#xff0c;进程是程序执行的基本单位。每个进程都有自己的状态&#xff0c;这些…...

web.py使用时报错AttributeError: No template named image_window

在使用python的web.py框架做前后端时遇到问题。 问题代码主要如下&#xff0c;当加上main(iamge_name)这行代码后就会报错。报错信息包含两个&#xff1a;第一是找不到image_window模板&#xff1b;第二是gbk无法解码... class ImageWindow:def GET(self, image_name):main(i…...

2025年度消费新潜力白皮书470+份汇总解读|附PDF下载

原文链接&#xff1a;https://tecdat.cn/?p42178 过去一年&#xff0c;消费市场在政策驱动与技术迭代中呈现结构性变革。社零总额达487,895亿元&#xff0c;实物商品网零额占比27%&#xff0c;线上渠道成为增长引擎。本报告从食品饮料、美妆护肤、家电数码、服饰户外四大核心领…...

全平台开源电子书阅读器推荐,支持多端同步+AI朗读!支持epub/mobi/azw3/pdf常见电子书格式!

Readest是一款好用的免费阅读工具&#xff0c;界面干净不花哨&#xff0c;特别适合喜欢专心读书的朋友。这个软件是经典阅读软件Foliate的全新升级版本&#xff0c;用最新技术开发&#xff0c;能在手机、电脑&#xff08;包括苹果和Windows系统&#xff09;以及网页上顺畅使用。…...

创建Workforce

创建你的Workforce 3.3.1 简单实践 1. 创建 Workforce 实例 想要使用 Workforce&#xff0c;首先需要创建一个 Workforce 实例。下面是最简单的示例&#xff1a; from camel.agents import ChatAgent from camel.models import ModelFactory from camel.types import Model…...

关于光谱相机的灵敏度

一、‌灵敏度的核心定义‌ ‌光谱灵敏度&#xff08;单色灵敏度&#xff09;‌ 描述光谱相机对单色辐射光的响应能力&#xff0c;即探测器对特定波长入射光的输出信号强度与入射光功率的比值。 例如&#xff0c;若在680nm波长下的光谱灵敏度较高&#xff0c;则表示该相机对此…...

【Redis】二、Redis常用数据类型命令学习

目录 一、String 1. SET、GET&#xff1a;设置与读取键值对&#xff1a; 2. DEL&#xff1a;删除键 3. INCR、DECR&#xff1a;自增 / 自减&#xff08;常用于计数器&#xff09; 4. APPEND&#xff1a;内容追加 5. EXPIRE&#xff1a;设置过期时间 / 查看剩余时间&#x…...

HarmonyOS基础组件:Button三种类型的使用

简介 HarmonyOS在明年将正式不再兼容Android原生功能&#xff0c;这意味着对于客户端的小伙伴不得不开始学习HarmonyOS开发语言。本篇文章主要介绍鸿蒙中的Button使用。 HarmonyOS中的Button相较于Android原生来说&#xff0c;功能比较丰富&#xff0c;扩展性高&#xff0c;减…...

RT_Thread——快速入门

文章目录 一、RT-Thread 目录结构二、核心文件三、移植时涉及的文件3.1 CPU 部分3.2 BSP 部分 四、内存管理五、启动流程及main函数5.1 启动流程5.2 关键函数速览5.3 main 函数示例 六、数据类型和编程规范6.1 数据类型6.2 函数名6.3 结构体定义6.4 注释规范 七、使用模拟器运行…...

Java 参数值传递机制

一个很经典的问题: java的方法入参 是值传递还是地址传递&#xff1f; 答案是&#xff1a;值传递。 今天排查一个生产问题&#xff0c;数据库链接资源没有关闭。 大致代码逻辑如下&#xff1a; try{Preparestatement ps null;String sql "select * from tableA wher…...

Redis 的 key 的过期策略是怎么实现的

在 Redis 中&#xff0c;有一个 expire 命令&#xff0c;用来设置某个 key 的过期时间&#xff0c;当超过这个时间后&#xff0c;这个 key 就被删除了&#xff0c;我们也就获取不到了&#xff0c;但是 Redis 是如何做到对于每一个设置了过期时间的 key 都能按时删除的呢&#x…...

ROG NUC 2025 :狂暴而冷静的小猛兽

在今年1 月的 CES 展会上&#xff0c;华硕首次披露了ROG NUC 2025&#xff0c;就以突破性紧凑设计桌面级超强性能配置&#xff0c;引发全球科技媒体和游戏爱好者的热议。蛰伏数月&#xff0c;蓄力进化&#xff01; 华硕自承接英特尔NUC产品线以来&#xff0c;就一直致力于重塑迷…...

origin绘图之【如何将多条重叠、高度重叠的点线图、折线图分开】

在使用 Origin 进行数据可视化时&#xff0c;尤其是在绘制多组数据的折线图或点线图时&#xff0c;我们经常会遇到这样的问题&#xff1a;多条曲线重叠严重&#xff0c;难以区分&#xff0c;导致图形信息密集、可读性差&#xff0c;影响图表的传达效果。 那么&#xff0c;我们该…...

2025第一届轩辕杯--Crypto--WriteUp

2025第一届轩辕杯–Crypto–WriteUp Crypto easyrsa task e 65537 n 1000000000000000000000000000156000000000000000000000000005643 c 418535905348643941073541505434424306523376401168593325605206exp from Crypto.Util.number import inverse, long_to_bytese …...

人工智能范式:技术革命下的认知重构

当生成式AI能够自主创作内容、设计解决方案甚至编写程序时&#xff0c;我们正在见证的不仅是工具革新&#xff0c;更是一场认知范式的根本转变。人工智能范式正在重塑人类理解世界、解决问题和创造价值的基本方式——这种转变将重新定义未来十年的职业逻辑与知识体系。 一、范…...

python训练营打卡第30天

模块和库的导入 知识点回顾&#xff1a; 导入官方库的三种手段导入自定义库/模块的方式导入库/模块的核心逻辑&#xff1a;找到根目录&#xff08;python解释器的目录和终端的目录不一致&#xff09; 一、导入官方库 1.标准导入&#xff1a;导入整个库 import mathprint(&quo…...

第29天-python实现mysql数据增删改查

想用Python和Tkinter实现一个MySQL数据库的增删改查应用。首先,我需要确定用户的需求是什么。他们可能想要一个图形界面,方便操作数据库,而不需要直接写SQL语句。用户可能对Python和Tkinter有一定了解,但对如何整合数据库操作可能不太熟悉。 首先,我应该考虑如何设计界面。…...

2025.05.21华为暑期实习机考真题解析第三题

📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围OJ 03. GPU资源租赁优化 问题描述 A先生是一家云计算服务商的资源调度负责人,负责管理公司的GPU资源租赁业务。公司拥有多个高性能GPU核心,并按时间段出租给不同客户使用。每个客户有…...

Datawhale 5月llm-universe 第4次笔记

第四章 构建RAG应用 envs 在 Conda 中&#xff0c;envs 目录是用来存放虚拟环境的地方。 也就是说&#xff0c;你在运行&#xff1a; onda create -n llm-universe python3.10 时&#xff0c;Conda 就会在这个路径下创建一个新的文件夹&#xff1a; makefile D:\Users\…...

滑窗问题实验LC2653(一次遍历维持窗口元素保持排序)

在只有一次遍历&#xff08;即滑窗每向右移动一步只处理新增元素和删除旧元素&#xff09;的前提下&#xff0c;要维持当前窗口内元素的全局“可排序”结构 问题背景&#xff1a;LeetCode 2653 - 滑动子数组的美丽值 题目要求在大小为 k 的滑动窗口中&#xff0c;找到第 x 小…...

PHP学习笔记(八)

返回值 值通过可选参数的返回语句返回 return的使用 函数不能返回多个值&#xff0c;但可以通过返回一个数组来得到类似的效果 函数返回一个引用&#xff0c;必须在函数声明和指派返回值给一个变量时都使用引用运算符&&#xff1a; 可变函数 PHP支持可变函数的概念。意味…...

【react18】在styled-components中引入图片报错

在styled-components项目中&#xff0c;遇到背景图片显示不出来的问题。图片的确是引入正确&#xff0c;但是webpack解析路径是有问题的 效果展示 以下这两种写法都不行&#xff0c;无法生效 export const HeaderNavLeft styled.h1width: 176px;height: 69px;background: ur…...

693SJBH基于.NET的题库管理系统

计算机与信息学院 本科毕业论文&#xff08;设计&#xff09;开题报告 论文中文题目 基于asp.net的题库管理系统设计与实现 论文英文题目 Asp.net based database management system design and Implementation 学生姓名 专业班级 XXXXXX专业08 班 ⒈选题的背景和意…...

centos系统redis-dump安装

1. ​Ruby 环境​ Redis-dump 是一个 Ruby 工具&#xff0c;需先安装 Ruby 和 RubyGems。 安装依赖​&#xff1a; sudo yum install -y curl gpg2 gcc-c patch readline readline-devel zlib zlib-devel libyaml-devel libffi-devel openssl-devel make bzip2 autoconf aut…...

如何利用 Conda 安装 Pytorch 教程 ?

如何利用 Conda 安装 Pytorch 教程 &#xff1f; 总共分为六步走&#xff1a; &#xff08;1&#xff09;第一步&#xff1a;验证conda 环境是否安装好&#xff1f; 1) conda -V2) conda --version&#xff08;2&#xff09;第二步&#xff1a;查看现有环境 conda env list…...

FPGA降低功耗研究

FPGA降低功耗研究 首先要明白一点&#xff1a;我们的核心目标是在维持性能的前提下&#xff0c;通过工艺、架构、设计方法学和系统级策略的协同优化&#xff0c;降低动态功耗、静态功耗和短路功耗。 本篇文章则是聚焦于 FPGA 设计阶段 的功耗优化&#xff0c;主要从 RTL 代码设…...

软考 系统架构设计师系列知识点之杂项集萃(67)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之杂项集萃&#xff08;66&#xff09; 第108题 RISC&#xff08;精简指令系统计算机&#xff09;的特点不包括&#xff08;&#xff09;。 A. 指令长度固定&#xff0c;指令种类尽量少 B. 寻址方式尽量丰富&#xff…...

第十节第三部分:常见API:传统时间:Date日期类、SimpleDateFormat

Date日期类常用方法 时间格式常用符号 Date日期类总结 为什么用SimpleDateFormat SimpleDateFormat常见方法 SimpleDateFormat解析字符串时间成为日期对象 SimpleDateFormat总结 代码&#xff1a; 代码一&#xff1a;Date日期类 package com.itheima.Time;import java.util.D…...

Python学习Day1:安装

Python的安装 1.安装python 打开python的官网&#xff0c;我们下载3.6.8版本Python Release Python 3.6.8 | Python.org 根据自己的电脑来下载对应的python版本 如图所示&#xff1a; 我已经下载完成&#xff0c;就是这样&#xff0c; 安装页面的第一排是自动安装&#xf…...

Java安全-Servlet内存马

内存马简介 内存马是指将恶意代码注入到内存中&#xff0c;达到无文件落地的效果&#xff0c;使得被攻击方难以察觉。由于是无文件的形式&#xff0c;可以绕过部分基于文件检测的杀软。而 Servlet 内存马是基于 Java Servlet 技术&#xff0c;动态将恶意代码注入到 Tomcat 内存…...

Mariadb cpu 93% 问题

最近项目遇到cpu平均使用率93% 一、登录数据库服务查看具体情况 可以看到服务器负载很高&#xff0c;&#xff0c;mariadb CPU使用已达到接近380.4% &#xff08;因为是8核&#xff0c;所以会有超过100%的情况&#xff09;。如下图&#xff1a; 二、排查是否有耗时较长sql 在…...

LeetCode222_完全二叉树的结点个数

LeetCode222_完全二叉树的结点个数 标签&#xff1a;#位运算 #树 #二分查找 #二叉树Ⅰ. 题目Ⅱ. 示例 0. 个人方法 标签&#xff1a;#位运算 #树 #二分查找 #二叉树 Ⅰ. 题目 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&…...

linux 查看java的安装路径

一、验证Java安装状态 java -version正常安装会显示版本信息&#xff1a; openjdk version "1.8.0_65" OpenJDK Runtime Environment (build 1.8.0_65-b17) OpenJDK 64-Bit Server VM (build 25.65-b01, mixed mode)二、检查环境变量配置 若已配置JAVA_HOME&#…...

day32 python解释性库PDPbox

目录 一、初识PDPbox官方文档 二、准备鸢尾花数据集和训练模型 三、使用PDPbox进行解释性分析 1. 导入类并实例化对象 2. 调用plot方法绘制图形 3. 探索返回值的意义 四、总结与感悟 作为一名正在学习机器学习的学生&#xff0c;今天要学习一个非常有趣的库——PDPbox&am…...

LVLM-AFAH论文精读

Basic Information 标题&#xff1a;Your Large Vision-Language Model Only Needs A Few Attention Heads For Visual Grounding作者&#xff1a;Seil Kang&#xff0c; Jinyeong Kim&#xff0c; Junhyeok Kim&#xff0c; Seong Jae Hwang机构&#xff1a;Yonsei Universit…...

蓝桥杯3503 更小的数

问题描述 小蓝有一个长度均为 n 且仅由数字字符 0∼9 组成的字符串&#xff0c;下标从 0 到 n−1&#xff0c;你可以将其视作是一个具有 n 位的十进制数字 num&#xff0c;小蓝可以从 num 中选出一段连续的子串并将子串进行反转&#xff0c;最多反转一次。 小蓝想要将选出的子…...

5.21本日总结

一、英语 复习list4list26 二、数学 学完14讲&#xff0c;1000题13讲写完 三、408 学习计网5.3剩余内容 四、总结 高数本月结束知识点学习&#xff0c;15讲知识点与14讲的题目同步进行。408剩余两本书要加快学习进度。 五、明日计划 英语&#xff1a;复习lsit5list25 …...

【25软考网工】第七章(3) UOS Linux防火墙配置和Web应用服务配置

博客主页&#xff1a;christine-rr-CSDN博客 ​​​专栏主页&#xff1a;软考中级网络工程师笔记 ​​​​ 大家好&#xff0c;我是christine-rr !目前《软考中级网络工程师》专栏已经更新三十多篇文章了&#xff0c;每篇笔记都包含详细的知识点&#xff0c;希望能帮助到你&am…...

Python数据分析基础

Python数据分析入门 介绍 在这个教程中&#xff0c;我们将学习如何使用Python来进行基本的数据分析。 安装必要的库 为了开始&#xff0c;你需要安装以下Python库&#xff1a; NumPyPandasMatplotlib 示例代码 import numpy as np import pandas as pd import matplotli…...

spring cloud config更新配置

在开发微服务时&#xff0c;往往需要有开发环境、测试环境和生产环境&#xff0c;手动修改配置环境是一件很麻烦的事情&#xff0c;因此&#xff0c;这里使用spring cloud config管理配置环境。要使用spring cloud config&#xff0c;需要先在GitHub搭建一个仓库。 一、仓库搭…...