第一章:什么是 SCons 和 SConscript?

核心概念

SCons 是一个现代化的构建工具,用于自动化软件构建过程,类似于 Make 但功能更强大、语法更简洁。

  • SConstruct:是 SCons 的主配置文件,通常在项目根目录,相当于 Makefile
  • SConscript:是子配置文件,用于组织大型项目,可被 SConstruct 或其他 SConscript 包含
  • 构建过程:将源代码转换为可执行文件的过程(编译、链接等)

SCons 使用 Python 语法,因此如果你熟悉 Python,学习 SCons 会更容易。

第一个示例

创建一个最简单的 SConscript:

# 编译hello.c并生成可执行文件hello
Program('hello.c')

这个简单的脚本告诉 SCons:编译 hello.c 文件并生成同名可执行文件。

练习题

  1. 什么是 SCons?它的主要作用是什么?
  2. SConstruct 和 SConscript 有什么区别?
  3. 写出一个 SConscript 脚本,用于编译 main.c 生成名为 app 的可执行文件。

答案详解

1.SCons 是一个构建工具,主要作用是自动化软件的编译、链接等构建过程,简化项目管理,提高开发效率。与传统的 Make 相比,它语法更简洁,功能更强大,且跨平台性更好。

2.区别在于:

  • SConstruct 是主配置文件,是 SCons 的入口点
  • SConscript 是子配置文件,用于组织大型项目
  • 一个项目通常有一个 SConstruct,可能有多个 SConscript
  • SConstruct 可以包含并执行 SConscript

3.实现代码:

# 编译main.c生成名为app的可执行文件
Program('app', ['main.c'])

这里Program是 SCons 的一个构建器 (builder),第一个参数是输出文件名,第二个参数是源文件列表。

第二章:SConscript 基本语法

核心概念

SConscript 使用 Python 语法,同时提供了一些 SCons 特有的函数和变量:

  • 构建器 (Builder):如Program(生成可执行文件)、Library(生成库文件) 等
  • 路径处理GetCurrentDir()获取当前目录
  • 文件匹配Glob()匹配符合模式的文件
  • 依赖管理:处理文件间的依赖关系

常用函数

Program(target, sources):编译源文件生成可执行文件

Program('myapp', ['main.c', 'utils.c'])

Library(target, sources):生成库文件

Library('mylib', ['func1.c', 'func2.c'])

GetCurrentDir():获取当前目录路径

current_dir = GetCurrentDir()
print(f"当前目录: {current_dir}")

Glob(pattern):匹配文件

# 获取所有.c文件
c_files = Glob('*.c')
# 获取src目录下所有.c文件
src_files = Glob('src/*.c')

Return(value):返回值给父脚本

src_files = Glob('*.c')
Return('src_files')  # 将src_files返回给包含此脚本的父脚本

练习题

1.请写出一段 SConscript 代码,实现以下功能:

获取当前目录下所有的.c 文件

获取 src 目录下所有的.c 文件

将这两部分文件合并到一个列表中

打印出收集到的所有文件路径

2.

编写一个完整的 SConscript 脚本,实现以下功能:

编译 math 目录下所有的.c 文件生成名为 libmath 的静态库

编译 main.c 文件,并链接 libmath 库生成名为 calculator 的可执行文件

确保编译器能找到 math 目录下的头文件

答案详解

1. 实现代码:

# 获取当前目录下所有的.c文件
current_c_files = Glob('*.c')# 获取src目录下所有的.c文件
src_c_files = Glob('src/*.c')# 合并两个文件列表
all_c_files = current_c_files + src_c_files# 打印收集到的文件路径
print("收集到的C源文件:")
for file in all_c_files:print(f"- {file}")

解析

  • Glob(pattern)是 SCons 提供的文件匹配函数,用于查找符合模式的文件
  • *.c表示匹配当前目录下所有以.c 结尾的文件
  • src/*.c表示匹配 src 子目录下所有以.c 结尾的文件
  • +运算符用于合并两个列表(Python 语法)
  • 通过 for 循环遍历并打印所有文件路径,方便查看收集结果

2.实现代码:

# 编译math目录下所有.c文件生成libmath静态库
# Library是SCons的库文件构建器,第一个参数是库名,第二个是源文件
math_library = Library('math', Glob('math/*.c'))# 编译main.c并链接libmath库生成calculator可执行文件
# Program是SCons的可执行文件构建器
# CPPPATH指定头文件搜索路径,确保编译器能找到math目录下的头文件
# LIBS指定要链接的库文件
Program(target='calculator',          # 输出的可执行文件名source=['main.c'],            # 源文件CPPPATH=['math'],             # 头文件搜索路径LIBS=['math']                 # 要链接的库
)

解析

  • Library('math', Glob('math/*.c')):生成名为 libmath 的静态库(在 Linux 下实际生成 libmath.a,Windows 下生成 math.lib)
  • Glob('math/*.c'):自动收集 math 目录下所有的 C 源文件
  • CPPPATH=['math']:告诉编译器在 math 目录中查找头文件(相当于 gcc 的 - I 选项)
  • LIBS=['math']:指定链接名为 math 的库(SCons 会自动处理库文件的路径和命名规则)
  • 这种分离编译的好处是:如果 math 目录下的文件没有修改,再次构建时不会重新编译,提高构建效率

第三章:变量和配置

核心概念

在 SConscript 中,变量用于存储配置信息、文件列表、编译选项等:

  • 路径变量:存储目录路径,如源文件目录、头文件目录
  • 文件列表变量:存储源文件列表
  • 编译选项变量:如CPPPATH(头文件搜索路径)、CFLAGS(C 编译选项)
  • 条件变量:根据不同平台或配置定义不同的变量值

示例代码

# 定义变量
cwd = GetCurrentDir()  # 当前目录
src_files = []         # 源文件列表
inc_paths = [cwd, cwd + '/include']  # 头文件路径# 添加源文件
src_files += Glob('*.c')
src_files += Glob('src/*.c')# 设置编译选项
CPPPATH = inc_paths  # 头文件搜索路径
CFLAGS = '-Wall -O2'  # C编译选项# 根据条件修改变量
import os
if os.name == 'nt':  # Windows系统CFLAGS += ' -DWIN32'
else:  # 类Unix系统CFLAGS += ' -DUNIX'# 使用变量
Program('myapp', src_files, CPPPATH=CPPPATH, CFLAGS=CFLAGS)

练习题

1.解释以下变量的含义和作用:CPPPATHCFLAGSLIBSLIBPATH

2.编写一个 SConscript 脚本,实现以下功能:

  • 定义一个变量存储当前目录路径
  • 定义源文件列表,包含当前目录和 src 子目录下的所有.c 文件
  • 定义头文件搜索路径,包含当前目录、include 目录和 src/include 目录
  • 为 GCC 编译器设置编译选项:开启所有警告、将警告视为错误、优化级别为 O2
  • 为 Windows 系统添加宏定义_WIN32,为 Linux 系统添加宏定义_LINUX
  • 编译生成名为 myapp 的可执行文件

答案详解

1.各变量含义和作用:

CPPPATH:C 预处理器的头文件搜索路径列表。告诉编译器去哪里查找#include指令引用的头文件。
示例:CPPPATH=['include', 'src/include']相当于 gcc 的-Iinclude -Isrc/include选项。

CFLAGS:C 编译器的编译选项。用于设置警告级别、优化级别、宏定义等。
示例:CFLAGS='-Wall -O2'表示开启所有警告并使用 O2 级优化。

LIBS:需要链接的库文件列表。指定程序运行时依赖的库。
示例:LIBS=['m', 'pthread']表示链接数学库和线程库。

LIBPATH:库文件的搜索路径列表。告诉链接器去哪里查找需要链接的库文件。
示例:LIBPATH=['lib', '/usr/local/lib']相当于 gcc 的-Llib -L/usr/local/lib选项。

2. 实现代码:

# 导入os模块用于判断操作系统类型
import os# 定义当前目录路径变量
current_dir = GetCurrentDir()# 定义源文件列表:当前目录和src子目录下的所有.c文件
source_files = Glob('*.c') + Glob('src/*.c')# 定义头文件搜索路径
include_paths = [current_dir,           # 当前目录current_dir + '/include',  # include目录current_dir + '/src/include'  # src/include目录
]# 初始化编译选项
compile_flags = '-Wall -Werror -O2'  # 开启所有警告、警告视为错误、O2优化# 根据操作系统添加不同的宏定义
if os.name == 'nt':# Windows系统,添加_WIN32宏定义compile_flags += ' -D_WIN32'
else:# Linux或类Unix系统,添加_LINUX宏定义compile_flags += ' -D_LINUX'# 编译生成myapp可执行文件
Program(target='myapp',        # 目标可执行文件名source=source_files,   # 源文件列表CPPPATH=include_paths, # 头文件搜索路径CFLAGS=compile_flags   # 编译选项
)

解析

  • GetCurrentDir()是 SCons 提供的函数,用于获取当前脚本所在的目录路径
  • Glob('*.c')Glob('src/*.c')分别获取当前目录和 src 目录下的所有.c 文件,+运算符将两个列表合并
  • include_paths列表包含了所有需要搜索头文件的目录,确保编译器能找到所有#include的文件
  • compile_flags变量集合了所有编译选项,-Wall开启所有警告,-Werror将警告视为错误,-O2设置优化级别
  • -D选项用于定义宏,在预处理阶段生效,代码中可以通过#ifdef _WIN32等条件编译指令实现跨平台逻辑
  • os.name是 Python 的 os 模块提供的变量,用于判断操作系统类型('nt' 表示 Windows,'posix' 表示 Linux/Unix 等)

第四章:条件判断和依赖管理

核心概念

  • 条件判断:根据不同平台、架构或配置选项执行不同的构建逻辑
  • 依赖管理:处理代码中的依赖关系,如特定功能依赖于某个宏定义
  • 平台相关配置:为不同 CPU 架构、操作系统定制构建选项

常用函数和语法

GetDepend(dependencies):检查是否存在特定的依赖项

# 检查是否定义了RT_USING_SMP宏
if GetDepend(['RT_USING_SMP']):print("启用了SMP支持")

字典用于存储平台 / 架构相关配置

# 支持的架构和CPU
support_arch = {"arm": ["cortex-m3", "cortex-m4"],"risc-v": ["na900"]
}

条件性添加源文件

src = []
# 根据架构添加不同的源文件
if arch == "arm":src += Glob('arch/arm/*.c')
elif arch == "risc-v":src += Glob('arch/risc-v/*.c')

示例代码

# 导入必要的模块和配置
import os
from building import *# 获取平台和架构信息
platform = rtconfig.PLATFORM
arch = rtconfig.ARCH
cpu = rtconfig.CPU# 初始化变量
cwd = GetCurrentDir()
src = []
CPPPATH = [cwd, cwd + '/include']# 定义支持的架构和CPU
support_arch = {"arm": ["cortex-m3", "cortex-m4"],"risc-v": ["na900"]
}# 根据CPU类型设置不同的源文件
if arch in support_arch.keys() and cpu in support_arch[arch]:# 添加对应架构和CPU的源文件src += Glob('arch/' + arch + '/' + cpu + '/*.c')# 添加公共架构代码src += Glob('arch/' + arch + '/common/*.c')# 添加通用代码src += Glob('*.c')# 设置头文件路径CPPPATH.append(cwd + '/arch/' + arch + '/' + cpu)# 根据配置选项移除不需要的文件
if not GetDepend('RT_USING_MEMORY_PROTECTION'):# 移除内存保护相关文件SrcRemove(src, ['mpu.c'])# 定义编译选项
LOCAL_CFLAGS = '-Wall'# 创建构建组并返回
group = DefineGroup('core', src, depend = ['RT_USING_CORE'], CPPPATH = CPPPATH, LOCAL_CFLAGS = LOCAL_CFLAGS)Return('group')

答案详解

1. GetDepend()函数详解:

作用:检查是否存在指定的依赖项,主要用于判断是否定义了特定的宏或配置选项。

参数格式:接受一个列表作为参数,列表中包含要检查的依赖项名称(字符串类型)。
示例:GetDepend(['RT_USING_SMP', 'RT_USING_MMU'])

返回值:返回一个布尔值(True 或 False)。如果所有指定的依赖项都存在,则返回 True;否则返回 False。

使用场景

根据宏定义决定是否包含某些源文件

启用或禁用特定功能模块

为不同配置提供不同的编译选项
示例代码:

# 检查是否定义了RT_USING_MEMORY_PROTECTION宏
if GetDepend(['RT_USING_MEMORY_PROTECTION']):# 如果定义了,则添加内存保护相关的源文件src += ['mpu.c', 'memory_protect.c']
else:# 如果未定义,则添加普通内存管理文件src += ['memory.c']

这段代码根据是否启用内存保护功能,选择不同的源文件进行编译,实现了条件性构建。

实现代码:

# 定义不同编译器对应的启动文件
startup_files = {'gcc': 'startup_gcc.s','armcc': 'startup_armcc.s','iccarm': 'startup_iccarm.s'
}# 假设这些变量是从配置中获取的
platform = rtconfig.PLATFORM  # 当前使用的编译器
arch = rtconfig.ARCH          # 当前架构# 初始化源文件列表
src = []# 选择默认启动文件
if platform in startup_files.keys():selected_startup = startup_files[platform]# 如果是ARM架构且启用了SMP,则使用带smp的启动文件if arch == "arm" and GetDepend(['RT_USING_SMP']):# 替换文件名,添加_smp后缀(如startup_gcc.s -> startup_gcc_smp.s)selected_startup = selected_startup.replace('.s', '_smp.s')# 将选择的启动文件添加到源文件列表src.append(selected_startup)
else:# 如果编译器不被支持,打印警告信息print(f"警告:不支持的编译器 {platform},未添加启动文件")# 可以继续添加其他源文件
src += Glob('*.c')

解析

  • startup_files字典使用键值对存储不同编译器对应的启动文件,便于根据编译器类型快速查找
  • platform in startup_files.keys()用于检查当前编译器是否在支持的列表中,避免使用未定义的启动文件
  • GetDepend(['RT_USING_SMP'])检查是否启用了 SMP(对称多处理)功能,这是嵌入式系统中常见的配置选项
  • selected_startup.replace('.s', '_smp.s')通过字符串替换生成带 smp 的启动文件名,避免重复编写条件判断
  • src.append(selected_startup)将选择好的启动文件添加到源文件列表,参与后续的编译过程
  • 最后的src += Glob('*.c')将其他 C 源文件添加到列表中,完成源文件的收集

第五章:项目实战与综合应用

核心概念

综合前面所学的知识,我们可以理解和编写更复杂的 SConscript 脚本,主要包括:

  • 项目结构组织
  • 多平台 / 架构支持
  • 条件编译和配置管理
  • 构建组定义和返回

示例解析

让我们解析你提供的示例脚本,理解其工作原理:

from building import *
import os# 获取平台、架构和CPU信息
platform = rtconfig.PLATFORM
arch     = rtconfig.ARCH
cpu      = rtconfig.CPU# 初始化变量
cwd     = GetCurrentDir()
src     = []
CPPPATH = [cwd]# 定义支持的架构和CPU
support_arch  = {"arm": ["cortex-m3", "cortex-m4", "cortex-m7", "cortex-a", "cortex-r5", "cortex-r52", "cortex-m33"],"aarch64":["cortex-a"],"risc-v": ["na900"],"arc": ["em"],"arch_tricore": ["arch_tc3", "arch_tc4"],"RH850": ["rh850g3kh","rh850g4mh"],
}# 定义不同编译器对应的汇编文件
platform_file = {'armcc': 'rvds.S', 'gcc': 'gcc.S', 'iccarm': 'iar.S', 'mw': 'mw_gcc.S', 'armclang': 'rvds.S', 'ghs':'osa_ghs.S'
}# 根据CPU类型和配置修改汇编文件
if cpu == "cortex-m4":if GetDepend(['RT_USING_SMP']):platform_file[platform] = 'gcc_smp.S'else:platform_file[platform] = 'gcc.S'if cpu == "cortex-m3":if GetDepend(['RT_USING_SMP']):platform_file[platform] = 'iar_smp.S'else:platform_file[platform] = 'iar.S'if cpu == "cortex-m33":if GetDepend(['RT_USING_SMP']):platform_file[platform] = 'gcc_smp.S'else:platform_file[platform] = 'gcc.S'# 处理risc-v架构的CPU
if arch == 'risc-v':rv64 = ['virt64', 'c906']if cpu in rv64:cpu = 'rv64'# 根据平台和架构添加源文件
if platform in platform_file.keys():  # 检查是否支持当前平台if arch in support_arch.keys() and cpu in support_arch[arch]:# 汇编文件路径asm_path = 'arch/' + arch + '/' + cpu + '/*' + platform_file[platform]# 公共架构代码路径arch_common = 'arch/' + arch + '/' + 'common/*.c'# 添加所有源文件src += Glob('*.c') + Glob(asm_path) + Glob(arch_common)src += Glob('arch/' + arch + '/' + cpu + '/*.c')# 设置头文件路径CPPPATH = [cwd, cwd + '/arch/' + arch + '/' + cpu, cwd + '/include']# 处理特殊架构
if arch == "arch_tricore":src = ['arch/arch_tricore/osa_tricore.c']src += Glob('*.c')CPPPATH = [cwd, cwd + '/include']# 移除不需要的文件
if not GetDepend('RT_USING_MEMORY_PROTECTION'):SrcRemove(src, ['osa_mpu.c'])# 设置编译选项
LOCAL_CFLAGS = ''
if rtconfig.PLATFORM in ['gcc']:  # 仅对GCC设置LOCAL_CFLAGS = ' -Wall -Werror'# 定义构建组并返回
group = DefineGroup('osa', src, depend = ['RT_USING_OSA'], CPPPATH = CPPPATH, LOCAL_CFLAGS = LOCAL_CFLAGS)Return('group')

这个脚本的主要功能:

  1. 根据不同的 CPU 架构、类型和编译器选择合适的源文件
  2. 处理特殊的架构配置
  3. 根据是否启用内存保护功能决定是否包含相关文件
  4. 为 GCC 编译器设置特定的编译选项
  5. 定义一个名为 'osa' 的构建组并返回

练习题

1.在提供的示例脚本中,有这样一段代码:

if not GetDepend('RT_USING_MEMORY_PROTECTION'):SrcRemove(src, ['osa_mpu.c'])

请详细解释这段代码的作用、使用场景和工作原理。

2.如何修改示例脚本,使其支持一种新的 CPU 架构 "risc-v" 的 "c910" 型号?需要修改哪些部分?为什么?

3.解释DefineGroup()函数的各个参数的含义,并说明为什么在示例脚本的最后要使用Return('group')

答案详解

1. 代码解析:

if not GetDepend('RT_USING_MEMORY_PROTECTION'):SrcRemove(src, ['osa_mpu.c'])

作用:当未启用内存保护功能时,从源文件列表中移除内存保护相关的文件osa_mpu.c

使用场景:这是条件编译的典型应用,用于根据不同的功能配置包含或排除特定文件。在嵌入式系统中,内存保护(MPU)通常是可选功能,不是所有硬件平台都支持,也不是所有项目都需要。

工作原理

GetDepend('RT_USING_MEMORY_PROTECTION')检查是否定义了RT_USING_MEMORY_PROTECTION宏(通常在配置文件中定义)

not表示取反,如果未定义该宏(即不启用内存保护),则执行下面的语句

SrcRemove(src, ['osa_mpu.c'])是 SCons 提供的函数,用于从src列表中移除osa_mpu.c文件

这样,在后续的编译过程中,osa_mpu.c就不会被编译,避免了不必要的代码和可能的编译错误

好处:通过这种方式,可以用一个代码库支持不同的功能配置,无需为不同配置维护多个代码分支,提高了代码的复用性和可维护性。

2. 支持新 CPU 架构 "risc-v" 的 "c910" 型号的修改:

需要修改以下几个部分:

# 1. 在support_arch字典中添加c910支持
support_arch  = {# ... 其他架构保持不变"risc-v": ["na900", "c910"],  # 添加c910到risc-v支持列表# ... 其他架构保持不变
}# 2. 在risc-v架构处理部分添加c910的支持
if arch == 'risc-v':rv64 = ['virt64', 'c906', 'c910']  # 添加c910到rv64列表if cpu in rv64:cpu = 'rv64'  # 将c910归类为rv64架构# 3. 为c910添加特定的汇编文件配置(如果需要)
if arch == 'risc-v' and cpu == 'c910':# 根据是否启用SMP选择不同的汇编文件if GetDepend(['RT_USING_SMP']):platform_file[platform] = 'gcc_riscv_c910_smp.S'else:platform_file[platform] = 'gcc_riscv_c910.S'

修改原因

  1. 第 1 处修改:support_arch字典定义了脚本支持的架构和 CPU 型号,添加 c910 才能让脚本识别并处理这个 CPU 型号
  2. 第 2 处修改:risc-v 架构有 32 位和 64 位之分,c910 是 64 位 CPU,需要归类到 rv64 中以使用正确的编译选项
  3. 第 3 处修改:不同的 CPU 可能需要不同的汇编启动文件或硬件相关代码,这部分修改确保为 c910 选择正确的汇编文件

3. DefineGroup()函数及Return('group')解析:

DefineGroup()函数用于定义一个构建组,其参数含义如下:

group = DefineGroup('osa',               # 第一个参数:构建组的名称,用于标识这个模块src,                 # 第二个参数:源文件列表,包含该模块所有需要编译的文件depend = ['RT_USING_OSA'],  # depend参数:构建依赖条件,只有定义了RT_USING_OSA才会构建这个组CPPPATH = CPPPATH,   # CPPPATH参数:头文件搜索路径,供编译器查找头文件LOCAL_CFLAGS = LOCAL_CFLAGS  # LOCAL_CFLAGS参数:该模块专用的编译选项
)

'osa':构建组的名称,通常与模块名一致,便于在构建系统中识别和引用

src:该模块所有源文件的列表,包含 C 文件、汇编文件等

depend = ['RT_USING_OSA']:指定构建该模块的前提条件,只有当RT_USING_OSA宏被定义时,才会编译这个模块

CPPPATH:指定该模块所需的头文件路径,确保编译器能找到所有需要的头文件

LOCAL_CFLAGS:该模块专用的编译选项,不会影响其他模块

Return('group')的作用:

  • SConscript 作为子脚本,需要将定义好的构建组返回给调用它的父脚本(通常是 SConstruct 或其他 SConscript)
  • Return('group')表示将group变量的值传递给父脚本
  • 父脚本可以通过env.SConscript('path/to/SConscript')获取这个返回值,并将其整合到整个项目的构建流程中
  • 这种机制实现了模块化的构建配置,每个子模块负责定义自己的构建信息,再由父脚本统一组织,使大型项目的构建配置更加清晰和可维护

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/96082.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/96082.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/96082.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【深度学习】PyTorch从0到1——手写你的第一个卷积神经网络模型,AI模型开发全过程实战

引言本次准备建立一个卷积神经网络模型,用于区分鸟和飞机,并从CIFAR-10数据集中选出所有鸟和飞机作为本次的数据集。以此为例,介绍一个神经网络模型从数据集准备、数据归一化处理、模型网络函数定义、模型训练、结果验证、模型文件保存&#…

云计算核心技术之容器技术

一、容器技术 1.1、为什么需要容器 在使用虚拟化一段时间后,发现它存在一些问题:不同的用户,有时候只是希望运行各自的一些简单程序,跑一个小进程。为了不相互影响,就要建立虚拟机。如果建虚拟机,显然浪费就…

微信小程序通过uni.chooseLocation打开地图选择位置,相关设置及可能出现的问题

前言 uni.chooseLocation打开地图选择位置,看官方文档介绍的比较简单,但是需要注意的细节不少,如果没有注意可能就无法使用该API或者报错,下面就把详细的配置方法做一下介绍。 一、勾选位置接口 ①在uniapp项目根目录找到manif…

从财务整合到患者管理:德国医疗集团 Asklepios完成 SAP S/4HANA 全链条升级路径

目录 挑战 解决方案 详细信息 Asklepios成立于1985年,目前拥有约170家医疗机构,是德国大型私营诊所运营商。Asklepios是希腊和罗马神话中的医神。 挑战 Asklepios希望进一步扩大其作为数字医疗保健集团的地位。2020年9月,该公司与SNP合作…

高频PCB厂家及工艺能力分析

一、技术领先型厂商(适合高复杂度、高可靠性设计)这类厂商在高频材料处理、超精密加工和信号完整性控制方面具备深厚积累,尤其适合军工、卫星通信、医疗设备等严苛场景:深南电路:在超高层板和射频PCB领域是行业标杆&am…

AJAX 与 ASP 的融合:技术深度解析与应用

AJAX 与 ASP 的融合:技术深度解析与应用 引言 随着互联网技术的不断发展,AJAX(Asynchronous JavaScript and XML)和ASP(Active Server Pages)技术逐渐成为构建动态网页和应用程序的重要工具。本文将深入探讨AJAX与ASP的融合,分析其原理、应用场景以及在实际开发中的优…

MuMu模拟器Pro Mac 安卓手机平板模拟器(Mac中文)

原文地址:MuMu模拟器Pro Mac 安卓手机平板模拟器 MuMu模拟器 Pro mac版,是一款MuMuPlayer安卓模拟器,可以畅快运行安卓游戏和应用。 MuMu模拟器Pro搭载安卓12操作系统,极致释放设备性能,最高支持240帧画面效果&#…

Oracle维护指南

Part 1 Oracle 基础与架构#### **1.1 概述** - **Oracle 数据库版本历史与特性对比** - **版本演进**: - Oracle 8i(1999):支持 Internet 应用,引入 Java 虚拟机(JVM)。 - Oracle 9i&#…

如何为PDF文件批量添加骑缝章?

骑缝章跨越多页文件的边缘加盖,一旦文件被替换其中某一页或顺序被打乱,印章就无法对齐,能立刻发现异常。这有效保障了文件的完整性和真实性。它是纯净免费,不带广告,专治各类PDF盖章需求。用法极简:文件直接…

组合时代的 TOGAF®:为模块化企业重新思考架构

随着企业努力追求敏捷性和创新性,组合性正逐渐成为一项基础性的设计原则。组合思维改变了企业交付能力的方式 —— 更倾向于采用模块化、独立的组件,这些组件可以快速组装和重组。本文探讨了长期以来作为企业架构框架的TOGAF标准如何演进以支持组合架构。…

电子元器件-电阻终篇:基本原理,电阻分类及特点,参数/手册详解,电阻作用及应用场景,电阻选型及实战案例

目录 一、基本原理 1.1 介绍 1.2 计算公式​编辑 1.3 单位 1.4 标称值 二、分类及特点 2.1电阻分类及特点介绍 2.2常用电阻器件详细介绍 三、参数/数据手册解读 3.1 阻值 3.2 封装&功率 3.3 精度 3.5 额定电压 3.6 温度系数(TCR) 3.7 扩展 四、作用与使用场…

【软件测试】电商购物项目-各个测试点整理(六)

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 1、优惠券测试点 …

心路历程-启动流程的概念

我们之前已经安装过系统,其实兴奋的内心已经无以言表; 记得刚开始的那份喜悦是没办法演说的;可是高兴之余,好像突然又心情EMO了; 为何呢?因为系统装完了,你也不知道能够干什么; 所以…

Kubernetes Ingress实战:从环境搭建到应用案例

目录 一、概述 版本对比图 二、 Ingress应用案例 2.1 环境准备 2.2 验证-NodePort模式 设置Http代理 2.3 验证-LoadBalancer模式 修改ARP模式,启用严格ARP模式 搭建metallb支持LoadBalancer 普通的service测试 ingress访问测试: 一、概述 Ser…

项目发布上线清单

说明:博主想整理一份项目发布上线的清单,在每次发布上线前,对照清单一一核对,避免遗漏(往事不堪回首),欢迎大家补充。 前端是否有与后端协同发布的接口? 如果有,先发前端…

HTB Information Gathering - Web Edition最后的测验

因为它没有DNS解析,,所以不要尝试去使用dns枚举所有枚举出来的子域,马上修改hosts文件,与ip和域名填好,因为它不依赖dns通过vhost子域爆破 爬虫登场 w*****.inlanefreight.htb:32508爬到之后不要去理会那个api,除了填答案,,,其他任何用处都没有,不要浪费时间后面就不能剧透了,可…

IDEA、Pycharm、DataGrip等激活破解冲突问题解决方案之一

Jetbranis旗下的软件破解冲突问题解决方案之一,不一定适用所有破解包 问题:在使用Pycharm破解包破解该软件后,同样是jetbranis旗下软件的Datagrip却失去了之前破解的效果,需要重新破解,重新成功破解datagrip后&#xf…

使用 uv管理 Python 虚拟环境:比conda更快、更轻量的现代方案

文章目录什么是 uv?安装 uv在线安装(推荐)Windows 系统Linux / macOS 系统离线安装步骤 1:获取二进制包步骤 2:解压并移动到可执行路径步骤 3:设置环境变量验证安装创建并激活虚拟环境创建虚拟环境输出示例…

课堂记忆项目开发日志

课堂记忆项目开发日志 日期: 2025年8月18日 1. 基础实现 项目目标: 创建一个动态、美观的“课堂记忆”页面,展示教师信息、教学成果、学生反馈、未来计划、教学成就和教学金句。 实现交互功能,包括按钮点击展开内容、图片点击弹出详细信息、图表展示数据。 技术栈: HTML5 C…

蓝桥杯算法之搜索章 - 7

大家好,不同的时间,相同的地点!又和大家见面了,接下来我将带来多源BFS的内容 通过多源BFS的学习,大家将对BFS理解更加深入! lets go! 前言 通过前面内容的学习,大家肯定已经对于BFS有了一定理解…