SConscript 腳本入門教程

第一章:什么是 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有了一定理解…