初步结构。
This commit is contained in:
20
.clang-format
Normal file
20
.clang-format
Normal file
@@ -0,0 +1,20 @@
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 4
|
||||
PointerAlignment: Left
|
||||
AccessModifierOffset: -4
|
||||
ReflowComments: true
|
||||
SpacesBeforeTrailingComments: 3
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterFunction: true
|
||||
AfterClass: true
|
||||
TabWidth: 4
|
||||
ColumnLimit: 130
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^<.*>'
|
||||
Priority: 1
|
||||
- Regex: '^".*"'
|
||||
Priority: 2
|
||||
12
.clangd
Normal file
12
.clangd
Normal file
@@ -0,0 +1,12 @@
|
||||
Hover:
|
||||
ShowAKA: Yes
|
||||
Diagnostics:
|
||||
UnusedIncludes: None # 禁用未使用头文件提示
|
||||
Suppress: [
|
||||
anon_type_definition, # 禁用匿名的typedef提示
|
||||
unused-variable, # 禁用未使用变量提示
|
||||
unused-function, # 禁用未使用函数提示
|
||||
unused-includes,
|
||||
]
|
||||
ClangTidy:
|
||||
Remove: misc-unused-alias-decls
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
build*
|
||||
145
.vscode/settings.json
vendored
Normal file
145
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
{
|
||||
"files.autoSave": "onFocusChange",
|
||||
"editor.fontSize": 14,
|
||||
"editor.fontFamily": "'Mononoki Nerd Font Mono', 'Mononoki Nerd Font Mono', 'Mononoki Nerd Font Mono'",
|
||||
"editor.wordWrap": "on",
|
||||
"terminal.integrated.fontFamily": "'Mononoki Nerd Font Mono'",
|
||||
"cmake.configureOnOpen": true,
|
||||
//"C_Cpp.intelliSenseEngine": "disabled",
|
||||
"cmake.debugConfig": {
|
||||
"console": "externalTerminal",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "-gdb-set charset utf-8",
|
||||
"text": "-gdb-set charset UTF-8"
|
||||
},
|
||||
{
|
||||
"description": "Enable gdb pretty-printing",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
],
|
||||
//"visualizerFile": "${workspaceRoot}/.vscode/qt6.natvis",
|
||||
},
|
||||
"cmake.configureArgs": [
|
||||
"-Wno-dev"
|
||||
],
|
||||
"cmake.configureSettings": {
|
||||
//"CMAKE_TOOLCHAIN_FILE": "${env:VCPKG_DIR}/scripts/buildsystems/vcpkg.cmake"
|
||||
"CMAKE_PREFIX_PATH": "C:/local"
|
||||
},
|
||||
"cmake.options.statusBarVisibility": "visible",
|
||||
"cmake.generator": "Ninja",
|
||||
"C_Cpp.default.compileCommands": "${workspaceRoot}/build/compile_commands.json",
|
||||
"C_Cpp.default.cppStandard": "c++17",
|
||||
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
|
||||
"editor.inlayHints.enabled": "off",
|
||||
"editor.unicodeHighlight.allowedLocales": {
|
||||
"ja": true,
|
||||
"zh-hant": true,
|
||||
"zh-hans": true
|
||||
},
|
||||
"files.associations": {
|
||||
"*.cfg": "json",
|
||||
"xstring": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"cctype": "cpp",
|
||||
"charconv": "cpp",
|
||||
"chrono": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"exception": "cpp",
|
||||
"filesystem": "cpp",
|
||||
"forward_list": "cpp",
|
||||
"fstream": "cpp",
|
||||
"functional": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"ios": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"iterator": "cpp",
|
||||
"limits": "cpp",
|
||||
"list": "cpp",
|
||||
"locale": "cpp",
|
||||
"map": "cpp",
|
||||
"memory": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"optional": "cpp",
|
||||
"ostream": "cpp",
|
||||
"ratio": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"string": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"thread": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"utility": "cpp",
|
||||
"vector": "cpp",
|
||||
"xfacet": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xiosbase": "cpp",
|
||||
"xlocale": "cpp",
|
||||
"xlocbuf": "cpp",
|
||||
"xlocinfo": "cpp",
|
||||
"xlocmes": "cpp",
|
||||
"xlocmon": "cpp",
|
||||
"xlocnum": "cpp",
|
||||
"xloctime": "cpp",
|
||||
"xmemory": "cpp",
|
||||
"xmemory0": "cpp",
|
||||
"xstddef": "cpp",
|
||||
"xtr1common": "cpp",
|
||||
"xtree": "cpp",
|
||||
"xutility": "cpp",
|
||||
"bit": "cpp",
|
||||
"compare": "cpp",
|
||||
"concepts": "cpp",
|
||||
"coroutine": "cpp",
|
||||
"format": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"bitset": "cpp",
|
||||
"complex": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"set": "cpp",
|
||||
"variant": "cpp",
|
||||
"expected": "cpp",
|
||||
"source_location": "cpp",
|
||||
"regex": "cpp",
|
||||
"*.in": "cpp",
|
||||
"deque": "cpp",
|
||||
"future": "cpp",
|
||||
"queue": "cpp",
|
||||
"resumable": "cpp",
|
||||
"any": "cpp",
|
||||
"codecvt": "cpp",
|
||||
"csignal": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numeric": "cpp",
|
||||
"random": "cpp",
|
||||
"shared_mutex": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"cfenv": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"ranges": "cpp",
|
||||
"typeindex": "cpp"
|
||||
}
|
||||
}
|
||||
30
CMakeLists.txt
Normal file
30
CMakeLists.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(ToolBox LANGUAGES CXX)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin/${CMAKE_BUILD_TYPE})
|
||||
|
||||
if (MSVC)
|
||||
add_compile_options(/utf-8)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND git rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE VERSION_GIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
execute_process(
|
||||
COMMAND git rev-parse --abbrev-ref HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE VERSION_GIT_BRANCH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
configure_file(ToolBox.h.in ToolBox.h)
|
||||
message(STATUS "Version file config to: ${CMAKE_CURRENT_BINARY_DIR}")
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_subdirectory(fileUpdater)
|
||||
366
LibUse.md
Normal file
366
LibUse.md
Normal file
@@ -0,0 +1,366 @@
|
||||
# Spdlog
|
||||
|
||||
## sink
|
||||
|
||||
spdlog定义了几种sinks用于不同场景(也可自定义)下的日志输出,sink中主要包含:
|
||||
|
||||
- `set_pattern(const std::string&)`:设置日志输出的内容格式。
|
||||
- `set_level(level_enum)`: 设置日志输出的最低等级。
|
||||
- `log(log_msg)`:由logger自动调用,外部不会主动调用。
|
||||
|
||||
## logger
|
||||
|
||||
一个logger对象中存储有多个sink,当调用*logger*的日志输出函数时,*logger*会调用自身存储的所有*sink*对象的log(log_msg) 函数进行输出。logger中主要包括:
|
||||
|
||||
- `set_pattern(const std::string&)`:设置logger包含的所有sink的日志输出内容格式。
|
||||
- `set_level(level_enum)`:设置logger日志输出最低等级,如果logger包含的sink没有设置日志等级的话,则会为其设置日志等级。
|
||||
- `log(level_enum level,log_msg content)`:按照level等级进行输出content,logger其中日志输出最低等级小于或等于level的sink会进行执行输出操作。
|
||||
- `trace(content,arg1,arg2…)`:按照trace等级进行输出,输出内容由content与后面的参数格式化而成。同类的函数还包括:debug/info/warn…。
|
||||
|
||||
## 输出格式pattern
|
||||
|
||||
通过`set_pattern`可设定日志格式,如`set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l](%@): %v");`
|
||||
|
||||
输出标记flag:
|
||||
|
||||
| flag | meaning | example |
|
||||
| ----- | ------------------------------------------------------------ | ---------------------------------------------------------- |
|
||||
| %v | 日志内容 | “my log test content” |
|
||||
| %t | 线程ID | “123” |
|
||||
| %P | 进程ID | “234” |
|
||||
| %n | 记录器Logger名 | “basicLogger” |
|
||||
| %l | 日志级别 | “debug”, “info”, etc |
|
||||
| %L | 日志级别简称 | “D”, “I”, etc |
|
||||
| %a | 星期几(简称) | “Thu” |
|
||||
| %A | 星期几 | “Thursday” |
|
||||
| %b | 月份简称 | “Aug” |
|
||||
| %B | 月份 | “August” |
|
||||
| %c | 日期时间 | “Thu Aug 23 15:35:46 2014” |
|
||||
| %C | 年(两位) | “14” |
|
||||
| %Y | 年 | “2014” |
|
||||
| %D %x | 日期简写 | “08/23/14” |
|
||||
| %m | 月份(数字) | “11” |
|
||||
| %d | 日(数组) | “29” |
|
||||
| %H | 小时(24制) | “23” |
|
||||
| %I | 小时(12制) | “11” |
|
||||
| %M | 分钟 | “59” |
|
||||
| %S | 秒 | “58” |
|
||||
| %e | 毫秒 | “678” |
|
||||
| %f | 微秒 | “056789” |
|
||||
| %F | 纳秒 | “256789123” |
|
||||
| %p | AM/PM | “AM” |
|
||||
| %r | 时间(12制) | “02:55:02 pm” |
|
||||
| %R | 时分(24制) | “23:55” |
|
||||
| %T %X | 时间(24制) | “23:55:59” |
|
||||
| %z | 时区(偏移) | “+02:00” |
|
||||
| %E | epoch(秒) | “1528834770” |
|
||||
| %% | 百分号 | “%” |
|
||||
| %+ | 默认格式 | “[2014-10-31 23:46:59.678] [mylogger] [info] Some message” |
|
||||
| %^ | start color range (can be used only once) | “[mylogger] [info(green)] Some message” |
|
||||
| %$ | end color range (for example %^[+++]%$ %v) (can be used only once) | [+++] Some message |
|
||||
| %@ | 文件名与行数 | my_file.cpp:123 |
|
||||
| %s | 文件名 | my_file.cpp |
|
||||
| %g | 文件名(含路径) | /some/dir/my_file.cpp |
|
||||
| %# | 行数 | 123 |
|
||||
| %! | 函数名 | my_func |
|
||||
| %o | 相对上一条记录的时间间隔(毫秒) | 456 |
|
||||
| %i | 相对上一条记录的时间间隔(微秒) | 456 |
|
||||
| %u | 相对上一条记录的时间间隔(纳秒) | 11456 |
|
||||
| %O | 相对上一条记录的时间间隔(秒) | 4 |
|
||||
|
||||
日志输出中要携带文件名、行数或函数名时,必须使用`SPDLOG_LOGGER_*`宏,且要激活对应的级别(哪些级别以上的日志会被记录):
|
||||
|
||||
```text
|
||||
// 记录INFO及以上级别日志
|
||||
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
SPDLOG_LOGGER_INFO(myLogger, "Support for floats {:03.2f}", 1.23456);
|
||||
SPDLOG_LOGGER_WARN(myLogger, "Easy padding in numbers like {:08d}", 12);
|
||||
```
|
||||
|
||||
### 对齐方式
|
||||
|
||||
每个flag都可携带对齐方式(最多支持64字符),
|
||||
|
||||
| align | meaning | example | result |
|
||||
| --------------- | ------- | ------- | -------- |
|
||||
| %<width><flag> | 右对齐 | %8l | " info" |
|
||||
| %-<width><flag> | 左对齐 | %-8l | "info " |
|
||||
| %=<width><flag> | 居中 | %=8l | " info " |
|
||||
|
||||
### 截断
|
||||
|
||||
通过!可设定对应输出的最大长度:
|
||||
|
||||
| align | meaning | example | result |
|
||||
| ---------------- | ------------ | ------- | ------ |
|
||||
| %<width>!<flag> | 右对齐且截断 | %3!l | “inf” |
|
||||
| %-<width>!<flag> | 左对齐且截断 | %-2!l | “in” |
|
||||
| %=<width>!<flag> | 居中且截断 | %=1!l | “i” |
|
||||
|
||||
## 字符串格式化fmt
|
||||
|
||||
spdlog中字符串格式化使用fmt([https://github.com/fmtlib/fmt](https://link.zhihu.com/?target=https%3A//github.com/fmtlib/fmt))库。
|
||||
|
||||
格式化方式:`{ [arg_id] [: (format_spec | chrono_format_spec)] }`
|
||||
|
||||
- arg_id:参数标识;
|
||||
- 忽略(为空时),依次对应每一个参数;
|
||||
- 索引(数字,从0开始),引用第几个索引;
|
||||
- 名称,命名参数;
|
||||
- format_spec:参数格式化方式(类型、对齐、填充等);
|
||||
|
||||
### Format Specification
|
||||
|
||||
格式化符说明:
|
||||
|
||||
```text
|
||||
format_spec ::= [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type]
|
||||
fill ::= <a character other than '{' or '}'>
|
||||
align ::= "<" | ">" | "^" // 左、右、居中对齐
|
||||
sign ::= "+" | "-" | " "
|
||||
width ::= integer | {[arg_id]} // 宽度:数字或指定的参数
|
||||
precision ::= integer | {[arg_id]} // 精度:数字或指定的参数
|
||||
type ::= "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F"
|
||||
| "g" | "G" | "o" | "p" | "s" | "x" | "X"
|
||||
```
|
||||
|
||||
`#`不同的转换下有不同的意义:
|
||||
|
||||
- 整数时,表示前面添加进制前缀,如0x, 0b等;
|
||||
- 浮点数时:总是有小数点(即使没有小数部分);
|
||||
|
||||
`L`只对数字有效,根据本地设置来输出:如,
|
||||
|
||||
```text
|
||||
auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
|
||||
// s == "1,234,567,890"
|
||||
```
|
||||
|
||||
格式化类型:
|
||||
|
||||
格式化类型:
|
||||
|
||||
| type | meaning |
|
||||
| ---- | ---------------------------------------- |
|
||||
| s | 字符串 |
|
||||
| c | 字符 |
|
||||
| b/B | 二进制 |
|
||||
| d | 数字(十进制) |
|
||||
| o | 八进制 |
|
||||
| x/X | 十六进制 |
|
||||
| a/A | 十六进制浮点数(p表示指数) |
|
||||
| e/E | 科学计数 |
|
||||
| f/F | 浮点数(包括NAN,INF),固定小数位数输出 |
|
||||
| g/G | 浮点数输出 |
|
||||
| p | 指针 |
|
||||
|
||||
示例:
|
||||
|
||||
```text
|
||||
fmt::format("{:*^30}", "centered"); // use '*' as a fill char
|
||||
// Result: "***********centered***********"
|
||||
|
||||
fmt::format("{:#04x}", 0);
|
||||
// Result: "0x00"
|
||||
|
||||
fmt::print(
|
||||
"┌{0:─^{2}}┐\n"
|
||||
"│{1: ^{2}}│\n"
|
||||
"└{0:─^{2}}┘\n", "", "Hello, world!", 20);
|
||||
┌────────────────────┐
|
||||
│ Hello, world! │
|
||||
└────────────────────┘
|
||||
```
|
||||
|
||||
## spdlog使用
|
||||
|
||||
spdlog默认日志输出级别是INFO。
|
||||
|
||||
默认情况下,日志是同步模式的,可通过以下方法开启异步模式:
|
||||
|
||||
```text
|
||||
size_t q_size = 4096; //queue size must be power of 2
|
||||
spdlog::set_async_mode(q_size);
|
||||
```
|
||||
|
||||
在异步模式下,日志先存入队列(队列占用的内存 = 设置的队列大小 * slot的大小, 64位系统下slot大小为104字节。),再由工作者线程从队列中取出并输出。当队列满时,会根据设定策略处理:
|
||||
|
||||
- 阻塞新来的日志,直到队列中有剩余空间(默认处理方式);
|
||||
- 丢弃新来的日志,需要如下设定策略:
|
||||
|
||||
```text
|
||||
spdlog::set_async_mode(q_size, spdlog::async_overflow_policy::discard_log_msg);
|
||||
```
|
||||
|
||||
## 异常处理
|
||||
|
||||
当输出日志时发生异常时,spdlog会向std::err 打印一条语句,为了避免输出的异常语句刷屏,打印频率被限制在每分钟一条。可通过set_error_handler来设定异常处理函数:
|
||||
|
||||
```text
|
||||
//can be set globaly or per logger(logger->set_error_handler(..))
|
||||
spdlog::set_error_handler([](const std::string& msg)
|
||||
{
|
||||
std::cerr << "my err handler: " << msg << std::endl;
|
||||
});
|
||||
```
|
||||
|
||||
## logger
|
||||
|
||||
默认情况下,spdlog的默认logger为输出到stdout:
|
||||
|
||||
```text
|
||||
# ifdef _WIN32
|
||||
auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
|
||||
# else
|
||||
auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
|
||||
# endif
|
||||
```
|
||||
|
||||
在使用完logger后,要关闭掉以释放(否则无再建立同名logger)
|
||||
|
||||
```text
|
||||
spdlog::drop_all(); // 关闭所有logger
|
||||
spd::drop("basic_logger"); // 关闭指定logger
|
||||
```
|
||||
|
||||
### 基础用法
|
||||
|
||||
spdlog中使用`{}`(里面可指定格式)作为格式化符
|
||||
|
||||
以下方式把日志输出到默认logger上:
|
||||
|
||||
```text
|
||||
//#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
spdlog::info("{:<30}", "left aligned");
|
||||
spdlog::warn("Easy padding in numbers like {:08d}", 12);
|
||||
spdlog::error("Some error message with arg: {}", 1);
|
||||
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||
|
||||
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
|
||||
spdlog::debug("This message should be displayed..");
|
||||
|
||||
// change log pattern
|
||||
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
|
||||
|
||||
// Compile time log levels
|
||||
// define SPDLOG_ACTIVE_LEVEL to desired level
|
||||
SPDLOG_TRACE("Some trace message with param {}", 42);
|
||||
SPDLOG_DEBUG("Some debug message");
|
||||
}
|
||||
```
|
||||
|
||||
### stdout日志
|
||||
|
||||
以彩色方式输出到标准输出设备上:
|
||||
|
||||
```text
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
|
||||
void stdout_example()
|
||||
{
|
||||
// create color multi threaded logger
|
||||
auto console = spdlog::stdout_color_mt("console");
|
||||
auto err_logger = spdlog::stderr_color_mt("stderr");
|
||||
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
|
||||
}
|
||||
```
|
||||
|
||||
### 文件日志
|
||||
|
||||
### 基本文件
|
||||
|
||||
最简单的日志文件:
|
||||
|
||||
```text
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
void basic_logfile_example()
|
||||
{
|
||||
try
|
||||
{
|
||||
auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
|
||||
}
|
||||
catch (const spdlog::spdlog_ex &ex)
|
||||
{
|
||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 循环文件
|
||||
|
||||
日志文件超过指定大小后,自动生成一个新的;并且只保留最多指定数量的日志文件:
|
||||
|
||||
```text
|
||||
#include "spdlog/sinks/rotating_file_sink.h"
|
||||
void rotating_example()
|
||||
{
|
||||
// Create a file rotating logger with 5mb size max and 3 rotated files
|
||||
auto max_size = 1024*1024 * 5;
|
||||
auto max_files = 3;
|
||||
auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files);
|
||||
}
|
||||
```
|
||||
|
||||
### 每日文件
|
||||
|
||||
每天指定时间生成一个新的日志文件:
|
||||
|
||||
```text
|
||||
#include "spdlog/sinks/daily_file_sink.h"
|
||||
void daily_example()
|
||||
{
|
||||
// Create a daily logger - a new file is created every day on 2:30am
|
||||
auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||
}
|
||||
```
|
||||
|
||||
## 示例
|
||||
|
||||
设定默认日志记录文件并在不同地方获取使用:
|
||||
|
||||
```text
|
||||
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/sinks/rotating_file_sink.h"
|
||||
|
||||
|
||||
void writeLog(int n) {
|
||||
for (int i = 0; i < n; ++i) {
|
||||
// 获取logger后输出日志
|
||||
auto myLogger = spdlog::get("baseLogger");
|
||||
myLogger->info("{}: Hello, {}!", i + 1, "World");
|
||||
myLogger->info("Welcome to spdlog!");
|
||||
myLogger->error("Some error message with arg: {}", 1);
|
||||
|
||||
// 带文件名与行号的日志输出
|
||||
SPDLOG_LOGGER_INFO(myLogger, "Support for floats {:03.2f}", 1.23456);
|
||||
SPDLOG_LOGGER_WARN(myLogger, "Easy padding in numbers like {:08d}", 12);
|
||||
|
||||
// 输出到默认日志中
|
||||
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||
spdlog::error("Some error message with arg: {}", 1);
|
||||
spdlog::warn("Easy padding in numbers like {:08d}", 12);
|
||||
spdlog::info("Support for floats {:03.2f}", 1.23456);
|
||||
}
|
||||
}
|
||||
|
||||
void testSPDLog() {
|
||||
// 设定日志最大100k,且最多保留10个
|
||||
auto myLogger = spdlog::rotating_logger_mt("baseLogger", "logs/basic.log", 1024 * 100, 10);
|
||||
spdlog::set_default_logger(myLogger);
|
||||
myLogger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l](%@): %v"); // 非通过宏输出的日志%@输出为空
|
||||
myLogger->set_level(spdlog::level::info);
|
||||
|
||||
myLogger->info("Hello, {}!", "World");
|
||||
|
||||
writeLog(10);
|
||||
}
|
||||
```
|
||||
11
README.md
Normal file
11
README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Tools
|
||||
|
||||
简易工具。
|
||||
|
||||
# strReplace
|
||||
|
||||
字符串替换。
|
||||
|
||||
# fileUpdater
|
||||
|
||||
文件更新工具。
|
||||
10
ToolBox.h.in
Normal file
10
ToolBox.h.in
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef FILEUPDATER_VERSION_H
|
||||
#define FILEUPDATER_VERSION_H
|
||||
|
||||
#define PROJECT_NAME "@PROJECT_NAME@"
|
||||
#define VERSION_GIT_COMMIT "@VERSION_GIT_HASH@"
|
||||
#define VERSION_GIT_BRANCH "@VERSION_GIT_BRANCH@"
|
||||
#define CMAKE_RUNTIME_OUTPUT_DIRECTORY "@CMAKE_RUNTIME_OUTPUT_DIRECTORY@"
|
||||
#define LIBRARY_OUTPUT_PATH "@LIBRARY_OUTPUT_PATH@"
|
||||
|
||||
#endif // FILEUPDATER_VERSION_H
|
||||
22
fileUpdater/CMakeLists.txt
Normal file
22
fileUpdater/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(fileUpdater LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if (MSVC)
|
||||
add_compile_options(/utf-8)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
find_package(cxxLibrary CONFIG REQUIRED)
|
||||
|
||||
add_executable(fileUpdater main.cpp)
|
||||
target_link_libraries(fileUpdater PRIVATE cxxLibrary::cxxLibrary)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS fileUpdater
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
512
fileUpdater/main.cpp
Normal file
512
fileUpdater/main.cpp
Normal file
@@ -0,0 +1,512 @@
|
||||
#include <CLI11.hpp>
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
#include <chrono>
|
||||
#include <ToolBox.h>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <spdlog/sinks/rotating_file_sink.h>
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <tinyxml2.h>
|
||||
#include <vector>
|
||||
#include <zoost.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
namespace xml = tinyxml2;
|
||||
|
||||
// 文件映射结构
|
||||
struct FileMapping {
|
||||
std::string m; // 源路径(相对工作目录)
|
||||
std::string n; // 目标路径(相对更新目录)
|
||||
std::string type; // "file" 或 "dir"
|
||||
std::vector<std::string> extensions; // 扩展名过滤列表
|
||||
bool isFile; // 是否为文件
|
||||
};
|
||||
|
||||
// 操作记录结构
|
||||
struct OperationRecord {
|
||||
fs::path source; // 源文件绝对路径
|
||||
fs::path target; // 目标文件绝对路径
|
||||
fs::path backup; // 备份路径
|
||||
bool exists; // 目标文件是否存在
|
||||
bool isNew; // 是否是新增文件
|
||||
};
|
||||
|
||||
class AutoUpdateTool
|
||||
{
|
||||
private:
|
||||
std::string configFile;
|
||||
fs::path workDir; // 工作目录(新文件所在)
|
||||
fs::path updateDir; // 更新目录(目标目录)
|
||||
fs::path backupDir; // 备份目录
|
||||
std::string fromType; // 源类型:"m" 或 "n"
|
||||
std::string toType; // 目标类型:"m" 或 "n"
|
||||
std::vector<FileMapping> mappings;
|
||||
std::shared_ptr<spdlog::logger> logger;
|
||||
std::string markerDir; // 标记目录名
|
||||
std::string logPath_;
|
||||
std::string logName_{"fileUpdater.log"};
|
||||
|
||||
public:
|
||||
AutoUpdateTool()
|
||||
{
|
||||
zoostPath zp;
|
||||
if (!zp.GetConfigFile("fileUpdater", logName_, logPath_)) {
|
||||
boost::nowide::cerr << "获取日志文件路径失败。" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
if (!zoostFs::exists((zp.GetParentPath(logPath_)))) {
|
||||
if (!zp.CreateConfigDir("fileUpdater")) {
|
||||
boost::nowide::cerr << "创建配置目录失败。" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(logPath_, 1024 * 1024 * 50, 3);
|
||||
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||
file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l]: %v");
|
||||
console_sink->set_pattern("[%H:%M:%S.%e] %^[%l] %v%$");
|
||||
|
||||
std::vector<spdlog::sink_ptr> sinks{file_sink, console_sink};
|
||||
logger = std::make_shared<spdlog::logger>(logName_, sinks.begin(), sinks.end());
|
||||
logger->set_level(spdlog::level::debug);
|
||||
spdlog::register_logger(logger);
|
||||
}
|
||||
|
||||
// 解析命令行参数
|
||||
bool parseArguments(int argc, char** argv)
|
||||
{
|
||||
auto notice = "使用特殊参数 default 可生成一个默认配置文件模板。";
|
||||
auto msg = fmt::format("fileUpdater - 文件更新工具 \n\n {} on {} at {} {}\n{}", VERSION_GIT_COMMIT, VERSION_GIT_BRANCH,
|
||||
__DATE__, __TIME__, notice);
|
||||
CLI::App app{msg};
|
||||
argv = app.ensure_utf8(argv);
|
||||
|
||||
app.add_option("-c,--config", configFile, "XML配置文件路径")->required()->check(CLI::ExistingFile);
|
||||
|
||||
app.add_option("-w,--work-dir", workDir, "工作目录(新文件所在)")->default_val(fs::current_path());
|
||||
|
||||
app.add_option("-u,--update-dir", updateDir, "更新目录(目标目录)")->required();
|
||||
|
||||
app.add_option("-b,--backup-dir", backupDir, "备份目录")->required();
|
||||
|
||||
app.add_option("-f,--from", fromType, "源类型 (m 或 n)")->required()->check(CLI::IsMember({"m", "n"}));
|
||||
|
||||
app.add_option("-t,--to", toType, "目标类型 (m 或 n)")->required()->check(CLI::IsMember({"m", "n"}));
|
||||
|
||||
try {
|
||||
app.parse(argc, argv);
|
||||
|
||||
// 规范化路径
|
||||
if (workDir.is_relative()) {
|
||||
workDir = fs::absolute(workDir);
|
||||
}
|
||||
if (updateDir.is_relative()) {
|
||||
updateDir = fs::absolute(updateDir);
|
||||
}
|
||||
if (backupDir.is_relative()) {
|
||||
backupDir = fs::absolute(backupDir);
|
||||
}
|
||||
|
||||
std::string rec;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
rec += argv[i];
|
||||
rec += " ";
|
||||
}
|
||||
|
||||
logger->debug("原始参数: {}", rec);
|
||||
logger->debug("CMD目录: {}", zoostFs::current_path().string());
|
||||
logger->info("工作目录: {}", workDir.string());
|
||||
logger->info("更新目录: {}", updateDir.string());
|
||||
logger->info("备份目录: {}", backupDir.string());
|
||||
logger->info("映射方向: {} -> {}", fromType, toType);
|
||||
|
||||
return true;
|
||||
} catch (const CLI::ParseError& e) {
|
||||
return app.exit(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 解析扩展名列表
|
||||
std::vector<std::string> parseExtensions(const std::string& extStr)
|
||||
{
|
||||
std::vector<std::string> exts;
|
||||
if (!extStr.empty()) {
|
||||
boost::split(exts, extStr, boost::is_any_of("|"));
|
||||
|
||||
// 确保扩展名以点开头
|
||||
for (auto& ext : exts) {
|
||||
boost::trim(ext);
|
||||
if (!ext.empty() && ext[0] != '.') {
|
||||
ext = "." + ext;
|
||||
}
|
||||
}
|
||||
}
|
||||
return exts;
|
||||
}
|
||||
|
||||
// 加载XML配置文件
|
||||
bool loadConfig()
|
||||
{
|
||||
if (configFile.empty()) {
|
||||
return false;
|
||||
}
|
||||
xml::XMLDocument doc;
|
||||
if (doc.LoadFile(configFile.c_str()) != xml::XML_SUCCESS) {
|
||||
logger->error("无法加载配置文件: {}", configFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto root = doc.FirstChildElement("AutoUpdate");
|
||||
if (!root) {
|
||||
logger->error("配置文件格式错误: 未找到AutoUpdate根节点");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 遍历所有FileMapping节点
|
||||
for (auto elem = root->FirstChildElement("FileMapping"); elem != nullptr;
|
||||
elem = elem->NextSiblingElement("FileMapping")) {
|
||||
|
||||
FileMapping mapping;
|
||||
mapping.m = elem->Attribute("m");
|
||||
mapping.n = elem->Attribute("n");
|
||||
mapping.type = elem->Attribute("type");
|
||||
mapping.isFile = (mapping.type == "file");
|
||||
|
||||
const char* extAttr = elem->Attribute("ext");
|
||||
std::string extStr = extAttr ? extAttr : "";
|
||||
mapping.extensions = parseExtensions(extStr);
|
||||
|
||||
mappings.push_back(mapping);
|
||||
}
|
||||
|
||||
logger->info("已加载 {} 个文件映射配置", mappings.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查文件或目录是否存在
|
||||
bool checkPath(const fs::path& path, bool isFile)
|
||||
{
|
||||
if (isFile) {
|
||||
return fs::is_regular_file(path);
|
||||
} else {
|
||||
return fs::is_directory(path);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前时间戳字符串
|
||||
std::string getCurrentTimestamp()
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto in_time_t = std::chrono::system_clock::to_time_t(now);
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::put_time(std::localtime(&in_time_t), "%Y_%m%d_%H%M%S_");
|
||||
ss << std::setfill('0') << std::setw(3) << ms.count();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// 验证映射并生成操作记录
|
||||
std::vector<OperationRecord> validateMappings()
|
||||
{
|
||||
std::vector<OperationRecord> records;
|
||||
|
||||
for (const auto& mapping : mappings) {
|
||||
// 确定源路径和目标路径
|
||||
std::string fromPath, toPath;
|
||||
if (fromType == "m") {
|
||||
fromPath = mapping.m;
|
||||
} else {
|
||||
fromPath = mapping.n;
|
||||
}
|
||||
|
||||
if (toType == "m") {
|
||||
toPath = mapping.m;
|
||||
} else {
|
||||
toPath = mapping.n;
|
||||
}
|
||||
|
||||
// 构建绝对路径
|
||||
fs::path sourceAbs = workDir / fromPath;
|
||||
fs::path targetAbs = updateDir / toPath;
|
||||
|
||||
// 步骤1:检查源路径是否存在
|
||||
if (!checkPath(sourceAbs, mapping.isFile)) {
|
||||
logger->error("源路径不存在: {}", sourceAbs.string());
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否是同一个文件
|
||||
if (fs::exists(targetAbs)) {
|
||||
try {
|
||||
if (fs::equivalent(sourceAbs, targetAbs)) {
|
||||
logger->warn("源和目标相同,跳过: {}", sourceAbs.string());
|
||||
continue;
|
||||
}
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
// 如果无法比较等价性,继续执行
|
||||
}
|
||||
}
|
||||
|
||||
OperationRecord record;
|
||||
record.source = sourceAbs;
|
||||
record.target = targetAbs;
|
||||
record.exists = fs::exists(targetAbs);
|
||||
record.isNew = !record.exists;
|
||||
|
||||
if (mapping.isFile) {
|
||||
// 文件处理
|
||||
records.push_back(record);
|
||||
} else {
|
||||
// 目录处理
|
||||
if (fs::is_directory(sourceAbs)) {
|
||||
for (const auto& entry : fs::recursive_directory_iterator(sourceAbs)) {
|
||||
if (fs::is_regular_file(entry)) {
|
||||
// 计算相对路径
|
||||
fs::path relPath = fs::relative(entry.path(), sourceAbs);
|
||||
|
||||
// 检查扩展名过滤
|
||||
if (!mapping.extensions.empty()) {
|
||||
std::string ext = entry.path().extension().string();
|
||||
if (std::find(mapping.extensions.begin(), mapping.extensions.end(), ext) ==
|
||||
mapping.extensions.end()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
OperationRecord dirRecord;
|
||||
dirRecord.source = entry.path();
|
||||
dirRecord.target = targetAbs / relPath;
|
||||
dirRecord.exists = fs::exists(dirRecord.target);
|
||||
dirRecord.isNew = !dirRecord.exists;
|
||||
records.push_back(dirRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
// 显示预执行计划
|
||||
void showPlan(const std::vector<OperationRecord>& records)
|
||||
{
|
||||
logger->info("=== 更新计划 ===");
|
||||
|
||||
int fileCount = 0;
|
||||
int dirCount = 0;
|
||||
int newCount = 0;
|
||||
int updateCount = 0;
|
||||
|
||||
for (const auto& record : records) {
|
||||
if (record.isNew) {
|
||||
newCount++;
|
||||
logger->info("[新增] {} -> {}", record.source.string(), record.target.string());
|
||||
} else {
|
||||
updateCount++;
|
||||
logger->info("[更新] {} -> {}", record.source.string(), record.target.string());
|
||||
}
|
||||
}
|
||||
|
||||
logger->info("=== 统计 ===");
|
||||
logger->info("新增文件: {} 个", newCount);
|
||||
logger->info("更新文件: {} 个", updateCount);
|
||||
logger->info("总计: {} 个文件", records.size());
|
||||
}
|
||||
|
||||
// 获取用户输入的标记目录
|
||||
bool getMarkerDirectory()
|
||||
{
|
||||
std::cout << "\n请输入标记目录名称(留空使用时间戳): ";
|
||||
std::getline(boost::nowide::cin, markerDir);
|
||||
|
||||
if (markerDir.empty()) {
|
||||
markerDir = getCurrentTimestamp();
|
||||
}
|
||||
|
||||
// 检查是否已存在
|
||||
fs::path markerPath = backupDir / markerDir;
|
||||
if (fs::exists(markerPath)) {
|
||||
logger->error("标记目录已存在: {}", markerPath.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
logger->info("标记目录: {}", markerDir);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 执行备份操作
|
||||
bool backupFile(const OperationRecord& record)
|
||||
{
|
||||
try {
|
||||
// 计算相对更新目录的路径
|
||||
fs::path relPath = fs::relative(record.target, updateDir);
|
||||
fs::path backupPath = backupDir / markerDir / relPath;
|
||||
|
||||
// 创建目标目录
|
||||
fs::create_directories(backupPath.parent_path());
|
||||
|
||||
// 复制文件
|
||||
fs::copy(record.target, backupPath, fs::copy_options::overwrite_existing);
|
||||
|
||||
logger->debug("已备份: {} -> {}", record.target.string(), backupPath.string());
|
||||
return true;
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
logger->error("备份失败: {} - {}", record.target.string(), e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 执行更新操作
|
||||
bool updateFile(const OperationRecord& record)
|
||||
{
|
||||
try {
|
||||
// 创建目标目录
|
||||
fs::create_directories(record.target.parent_path());
|
||||
|
||||
// 复制文件
|
||||
fs::copy(record.source, record.target, fs::copy_options::overwrite_existing);
|
||||
|
||||
if (record.isNew) {
|
||||
logger->info("[新增] {}", record.target.string());
|
||||
} else {
|
||||
logger->info("[更新] {}", record.target.string());
|
||||
}
|
||||
return true;
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
logger->error("更新失败: {} -> {} - {}", record.source.string(), record.target.string(), e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 执行更新
|
||||
bool executeUpdate(const std::vector<OperationRecord>& records)
|
||||
{
|
||||
int successCount = 0;
|
||||
int backupCount = 0;
|
||||
|
||||
logger->info("开始执行更新...");
|
||||
|
||||
// 先备份所有需要更新的文件
|
||||
for (const auto& record : records) {
|
||||
if (!record.isNew) { // 只有已存在的文件需要备份
|
||||
if (backupFile(record)) {
|
||||
backupCount++;
|
||||
} else {
|
||||
logger->warn("备份失败,但继续执行更新");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger->info("已备份 {} 个文件", backupCount);
|
||||
|
||||
// 执行更新
|
||||
for (const auto& record : records) {
|
||||
if (updateFile(record)) {
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 备份配置文件
|
||||
try {
|
||||
fs::path configBackupPath = backupDir / (markerDir + ".xml");
|
||||
fs::copy(configFile, configBackupPath, fs::copy_options::overwrite_existing);
|
||||
logger->info("配置文件已备份到: {}", configBackupPath.string());
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
logger->error("配置文件备份失败: {}", e.what());
|
||||
}
|
||||
|
||||
logger->info("=== 完成 ===");
|
||||
logger->info("成功更新 {} 个文件(共 {} 个)", successCount, records.size());
|
||||
logger->info("标记目录: {}", markerDir);
|
||||
|
||||
return successCount == records.size();
|
||||
}
|
||||
|
||||
// 运行主流程
|
||||
int run()
|
||||
{
|
||||
if (!loadConfig()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 验证并生成操作记录
|
||||
auto records = validateMappings();
|
||||
if (records.empty()) {
|
||||
logger->warn("没有找到需要更新的文件");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 显示计划
|
||||
showPlan(records);
|
||||
|
||||
// 获取标记目录
|
||||
if (!getMarkerDirectory()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 确认执行
|
||||
std::cout << "\n确认执行更新操作?(y/yes 确认,其他取消): ";
|
||||
std::string confirm;
|
||||
std::getline(boost::nowide::cin, confirm);
|
||||
|
||||
if (confirm != "y" && confirm != "yes") {
|
||||
logger->info("操作已取消");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 执行更新
|
||||
if (!executeUpdate(records)) {
|
||||
logger->error("更新过程中出现错误");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
zoostCommon::SetOutputU8();
|
||||
zoostCommon::SetStdLibrayU8();
|
||||
|
||||
if (argc > 1) {
|
||||
std::string specialStr = argv[1];
|
||||
if (specialStr == "default") {
|
||||
std::string defaultContent =
|
||||
R"(<?xml version="1.0" encoding="UTF-8"?>
|
||||
<AutoUpdate>
|
||||
<!-- 文件映射配置 -->
|
||||
<FileMapping m="a.txt" n="c/d/a.txt" type="file" ext=""/>
|
||||
<FileMapping m="" n="mm" type="dir" ext="txt|xml"/>
|
||||
</AutoUpdate>)";
|
||||
std::ifstream ifs("fileUpdater.xml");
|
||||
if (!ifs) {
|
||||
std::ofstream ofs("fileUpdater.xml");
|
||||
ofs << defaultContent;
|
||||
ofs.close();
|
||||
}
|
||||
ifs.close();
|
||||
boost::nowide::cout << "已生成默认配置文件: fileUpdater.xml" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
AutoUpdateTool tool;
|
||||
|
||||
if (tool.parseArguments(argc, argv)) {
|
||||
return tool.run();
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user