chore: baseline usable version
This commit is contained in:
130
libs/qwindowkit/qmsetup/.clang-format
Normal file
130
libs/qwindowkit/qmsetup/.clang-format
Normal file
@@ -0,0 +1,130 @@
|
||||
# References:
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
# https://code.qt.io/cgit/qt/qt5.git/tree/_clang-format
|
||||
|
||||
BasedOnStyle: LLVM
|
||||
|
||||
Standard: c++17
|
||||
|
||||
# 指针和引用的对齐方式。
|
||||
# 可能的值有:
|
||||
# PAS_Left (在配置中: Left) 指针左对齐。
|
||||
# PAS_Right (在配置中: Right) 指针右对齐。
|
||||
# PAS_Middle (在配置中: Middle) 指针中间对齐。
|
||||
PointerAlignment: Right
|
||||
|
||||
# public/protected/private 等访问修饰符偏移量
|
||||
AccessModifierOffset: -4
|
||||
|
||||
# 缩进长度
|
||||
IndentWidth: 4
|
||||
|
||||
# 连续空行的最大数
|
||||
MaxEmptyLinesToKeep: 999
|
||||
|
||||
# 在OC中的@property后面添加一个空格。例如:使用“@property (readonly)”而不是“@property(readonly)”
|
||||
ObjCSpaceAfterProperty: true
|
||||
|
||||
# OC块中所拍的字符数
|
||||
ObjCBlockIndentWidth: 4
|
||||
|
||||
# 取决于值, 语句“int f() { return 0; }”可以被放到一个单行。
|
||||
# 可能的值有:
|
||||
# SFS_None (在配置中: None) 从不合并方法或函数到单独的一行。
|
||||
# SFS_Empty (在配置中: Empty) 仅合并空的函数。
|
||||
# SFS_Inline (在配置中: Inline) 仅合并类中定义的方法或函数. 意味着 “empty”.
|
||||
# SFS_All (在配置中: All) 合并所有的方法适应单行.
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
|
||||
# 如果为真(true), 语句“if (a) return;” 能被放到单行。
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
|
||||
# 如果为真(true), 对齐注释。
|
||||
AlignTrailingComments: true
|
||||
|
||||
# 如果为真,对齐连续的宏定义
|
||||
AlignConsecutiveMacros: true
|
||||
|
||||
# 如果为真(true),将会在“[”之后和“]”之前插入空格。
|
||||
SpacesInSquareBrackets: false
|
||||
|
||||
# 如果为真(true), 将会在“(”之后和“)”之前插入空格。
|
||||
SpacesInParentheses : false
|
||||
|
||||
# 如果为真(true), 校准连续的声明。
|
||||
# 这将会校准连续多行的声明的名字。这将会导致像下面这样的格式:
|
||||
# int aaaa = 12;
|
||||
# float b = 23;
|
||||
# std::string ccc = 23;
|
||||
AlignConsecutiveDeclarations: false
|
||||
|
||||
# 如果为真(true),连续调整多行
|
||||
# 这将会调整连续行中的分配操作符。这将会导致像下面这样的格式:
|
||||
# int aaaa = 12;
|
||||
# int b = 23;
|
||||
# int ccc = 23;
|
||||
AlignConsecutiveAssignments: false
|
||||
|
||||
# 如果为假(false),移除分配操作符(=)前空格。
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
|
||||
# 如果为真(true), 将会在字面量容器中插入空格(例如 OC和Javascript的数组和字典字面量)。
|
||||
SpacesInContainerLiterals: false
|
||||
|
||||
# 缩进case标签
|
||||
IndentCaseLabels: true
|
||||
|
||||
# 如果表达式中包含函数调用,并且函数调用因为表达式太长被放到了下一行,是否缩进
|
||||
IndentWrappedFunctionNames: true
|
||||
|
||||
# 如果为真(true), 保持块的起始空行。
|
||||
# true: false:
|
||||
# if (foo) { vs. if (foo) {
|
||||
# bar();
|
||||
# bar(); }
|
||||
# }
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
|
||||
# 允许所有参数都被放在下一行
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
|
||||
# 使用C风格强制类型转换后,是否在中间添加一个空格
|
||||
SpaceAfterCStyleCast: true
|
||||
|
||||
# 在模板定义后换行
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
|
||||
# Tab长度
|
||||
TabWidth: 4
|
||||
|
||||
# 是否使用Tab
|
||||
UseTab: Never
|
||||
|
||||
# 在括号后对齐参数
|
||||
# someLongFunction(argument1,
|
||||
# argument2);
|
||||
AlignAfterOpenBracket: Align
|
||||
|
||||
# 名字空间内部缩进
|
||||
NamespaceIndentation: All
|
||||
|
||||
# 一行最长列数
|
||||
ColumnLimit: 100
|
||||
|
||||
# 按层次缩进宏定义
|
||||
IndentPPDirectives: AfterHash
|
||||
|
||||
# 预处理语句缩进为 2
|
||||
PPIndentWidth: 2
|
||||
|
||||
# 数组元素对齐
|
||||
AlignArrayOfStructures: Left
|
||||
|
||||
# 不对头文件排序
|
||||
SortIncludes: Never
|
||||
|
||||
FixNamespaceComments: false
|
||||
|
||||
StatementMacros: ['__qas_attr__', '__qas_exclude__', '__qas_include__']
|
||||
|
||||
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ]
|
||||
98
libs/qwindowkit/qmsetup/.gitignore
vendored
Normal file
98
libs/qwindowkit/qmsetup/.gitignore
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
# This file is used to ignore files which are generated
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
*~
|
||||
*.log
|
||||
*.autosave
|
||||
*.a
|
||||
*.core
|
||||
*.moc
|
||||
*.o
|
||||
*.obj
|
||||
*.orig
|
||||
*.rej
|
||||
*.so
|
||||
*.so.*
|
||||
*_pch.h.cpp
|
||||
*_resource.rc
|
||||
*.qm
|
||||
.#*
|
||||
*.*#
|
||||
core
|
||||
!core/
|
||||
tags
|
||||
.DS_Store
|
||||
.directory
|
||||
*.debug
|
||||
Makefile*
|
||||
*.prl
|
||||
*.app
|
||||
moc_*.cpp
|
||||
ui_*.h
|
||||
qrc_*.cpp
|
||||
Thumbs.db
|
||||
# *.res
|
||||
# *.rc
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
|
||||
# qtcreator generated files
|
||||
*.pro.user*
|
||||
|
||||
# xemacs temporary files
|
||||
*.flc
|
||||
|
||||
# Vim temporary files
|
||||
.*.swp
|
||||
|
||||
# Visual Studio generated files
|
||||
*.ib_pdb_index
|
||||
*.idb
|
||||
*.ilk
|
||||
*.pdb
|
||||
*.sln
|
||||
*.suo
|
||||
*.vcproj
|
||||
*vcproj.*.*.user
|
||||
*.ncb
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.vcxproj
|
||||
*vcxproj.*
|
||||
|
||||
# MinGW generated files
|
||||
*.Debug
|
||||
*.Release
|
||||
|
||||
# Python byte code
|
||||
__pycache__
|
||||
*.pyc
|
||||
|
||||
# Binaries
|
||||
# --------
|
||||
*.dll
|
||||
*.exe
|
||||
|
||||
.DS_Store
|
||||
*/.DS_Store
|
||||
|
||||
build-src*
|
||||
build/
|
||||
cmake-build*
|
||||
*.user
|
||||
*.lnk
|
||||
_workingDir*
|
||||
.vscode
|
||||
.idea
|
||||
.cache
|
||||
cache
|
||||
.vs
|
||||
out/
|
||||
CMakeSettings.json
|
||||
# /vcpkg
|
||||
/data
|
||||
/*.natvis
|
||||
|
||||
*.sublime-*
|
||||
setup-vcpkg.json
|
||||
setup-vcpkg-temp*
|
||||
4
libs/qwindowkit/qmsetup/.gitmodules
vendored
Normal file
4
libs/qwindowkit/qmsetup/.gitmodules
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
[submodule "syscmdline"]
|
||||
path = src/syscmdline
|
||||
url = ../../SineStriker/syscmdline.git
|
||||
branch = main
|
||||
46
libs/qwindowkit/qmsetup/CMakeLists.txt
Normal file
46
libs/qwindowkit/qmsetup/CMakeLists.txt
Normal file
@@ -0,0 +1,46 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
project(qmsetup VERSION 1.0.0.0)
|
||||
|
||||
# ----------------------------------
|
||||
# Configure Options
|
||||
# ----------------------------------
|
||||
option(QMSETUP_VCPKG_TOOLS_HINT "Install executables to tools directory" OFF)
|
||||
option(QMSETUP_STATIC_RUNTIME "Static link runtime libraries on Windows" ON)
|
||||
|
||||
# ----------------------------------
|
||||
# CMake Settings
|
||||
# ----------------------------------
|
||||
if(NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /manifest:no")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /manifest:no")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /manifest:no")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8")
|
||||
|
||||
if(NOT DEFINED CMAKE_DEBUG_POSTFIX)
|
||||
set(CMAKE_DEBUG_POSTFIX "d")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ----------------------------------
|
||||
# Project Variables
|
||||
# ----------------------------------
|
||||
set(QMSETUP_VERSION ${PROJECT_VERSION})
|
||||
set(QMSETUP_INSTALL_NAME ${PROJECT_NAME})
|
||||
set(QMSETUP_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
include(cmake/QMSetupAPI.cmake)
|
||||
|
||||
add_subdirectory(src)
|
||||
21
libs/qwindowkit/qmsetup/LICENSE
Normal file
21
libs/qwindowkit/qmsetup/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Stdware Collections
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
252
libs/qwindowkit/qmsetup/README.md
Normal file
252
libs/qwindowkit/qmsetup/README.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# QMSetup
|
||||
|
||||
**QMSetup** is a set of CMake Modules and Basic Libraries for C/C++ projects.
|
||||
|
||||
**This project is independent from Qt and other 3rdparty libraries.** Due to the fact that it encompasses some tools that need to be compiled, it's strongly not suggested to be included as a subproject.
|
||||
|
||||
## Features
|
||||
|
||||
### Helpful Functions
|
||||
+ Path, Version functions
|
||||
+ Target configuration functions
|
||||
+ Qt related functions
|
||||
|
||||
### Generators
|
||||
+ Generate Windows RC files, manifest files
|
||||
+ Generate MacOS Bundle info files
|
||||
+ Generate configuration header files
|
||||
+ Generate Git information header files
|
||||
|
||||
### Filesystem Utilities
|
||||
+ Reorganize header files
|
||||
+ Copy files and directories after build
|
||||
|
||||
### Install Utilities
|
||||
+ Deploy project dependencies and fix rpaths
|
||||
|
||||
### Extended Build Rules
|
||||
+ Create translations with **Qt Linguist** tools
|
||||
+ Create source files with **Protobuf** compiler
|
||||
+ Create documentations with **Doxygen**
|
||||
|
||||
## Support Platforms
|
||||
|
||||
+ Microsoft Windows
|
||||
+ Apple Macintosh
|
||||
+ GNU/Linux
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Required Packages
|
||||
|
||||
#### Windows
|
||||
|
||||
Windows deploy command acquires the shared library paths by reading the PE files and searching the specified paths so that it doesn't depend on `dumpbin` tool.
|
||||
|
||||
#### Unix
|
||||
|
||||
Unix deploy command acquires the shared library paths by running `ldd`/`otool` command and fixes the *rpath*s by runing the `patchelf`/`install_name_tool` command, make sure you have installed them.
|
||||
|
||||
```sh
|
||||
sudo apt install patchelf
|
||||
```
|
||||
|
||||
### Build System
|
||||
|
||||
+ C++ 17
|
||||
+ CMake 3.19
|
||||
|
||||
### Open-Source Libraries
|
||||
+ https://github.com/SineStriker/syscmdline
|
||||
+ https://github.com/jothepro/doxygen-awesome-css
|
||||
|
||||
## Integrate
|
||||
|
||||
### Clone
|
||||
|
||||
Via Https
|
||||
```sh
|
||||
git clone --recursive https://github.com/stdware/qmsetup.git
|
||||
```
|
||||
Via SSH
|
||||
```sh
|
||||
git clone --recursive git@github.com:stdware/qmsetup.git
|
||||
```
|
||||
|
||||
### Preinstall (Suggested)
|
||||
|
||||
#### Build & Install
|
||||
```sh
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=/path/to
|
||||
cmake --build build --target all
|
||||
cmake --build build --target install
|
||||
```
|
||||
|
||||
#### Import
|
||||
```sh
|
||||
cmake -Dqmsetup_DIR=/path/to/lib/cmake/qmsetup ...
|
||||
```
|
||||
```cmake
|
||||
find_package(qmsetup REQUIRED)
|
||||
```
|
||||
|
||||
### Sub-project
|
||||
|
||||
It still needs to be installed, but the installation occurs during the CMake Configure phase and is executed only once.
|
||||
|
||||
```cmake
|
||||
find_package(qmsetup QUIET)
|
||||
|
||||
if (NOT TARGET qmsetup::library)
|
||||
# Modify this variable according to your project structure
|
||||
set(_source_dir ${CMAKE_CURRENT_SOURCE_DIR}/qmsetup)
|
||||
|
||||
# Import install function
|
||||
include("${_source_dir}/cmake/modules/private/InstallPackage.cmake")
|
||||
|
||||
# Install package in place
|
||||
set(_package_path)
|
||||
qm_install_package(qmsetup
|
||||
SOURCE_DIR ${_source_dir}
|
||||
BUILD_TYPE Release
|
||||
RESULT_PATH _package_path
|
||||
)
|
||||
|
||||
# Find package again
|
||||
find_package(qmsetup REQUIRED PATHS ${_package_path})
|
||||
|
||||
# Update import path
|
||||
set(qmsetup_DIR ${_package_path} CACHE PATH "" FORCE)
|
||||
endif()
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Examples
|
||||
|
||||
You can use the functions in this library to greatly simplify several kinds of common build rules.
|
||||
|
||||
#### Generate Configuration Header
|
||||
```cmake
|
||||
qm_import(Preprocess)
|
||||
|
||||
qm_add_definition(FOO false)
|
||||
qm_add_definition(BAR 114514)
|
||||
qm_add_definition(BAZ "ABC" STRING_LITERAL)
|
||||
|
||||
qm_generate_config(${CMAKE_BINARY_DIR}/conf.h)
|
||||
```
|
||||
|
||||
#### Reorganize Include Directory
|
||||
```cmake
|
||||
qm_import(Preprocess)
|
||||
|
||||
qm_sync_include(src/core ${CMAKE_BINARY_DIR}/include/MyCore
|
||||
INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR}/MyCore
|
||||
)
|
||||
```
|
||||
|
||||
#### Sync Resource Files After Build
|
||||
```cmake
|
||||
qm_import(Filesystem)
|
||||
|
||||
qm_add_copy_command(${PROJECT_NAME}
|
||||
SOURCES
|
||||
file.txt
|
||||
dir_to_copy
|
||||
dir_contents_to_copy/
|
||||
DESTINATION .
|
||||
)
|
||||
```
|
||||
|
||||
#### Deploy Project And All Dependencies
|
||||
```cmake
|
||||
qm_import(Deploy)
|
||||
|
||||
qm_deploy_directory("${CMAKE_INSTALL_PREFIX}"
|
||||
COMMENT "Deploy project spectacularly"
|
||||
PLUGINS "iconengines/qsvgicon" "bearer/qgenericbearer"
|
||||
QML Qt QtQml
|
||||
PLUGIN_DIR share/plugins
|
||||
QML_DIR share/qml
|
||||
)
|
||||
```
|
||||
|
||||
#### Add Qt Translations
|
||||
```cmake
|
||||
qm_import(Translate)
|
||||
|
||||
qm_find_qt(LinguistTools)
|
||||
qm_add_translation(${PROJECT_NAME}_translations
|
||||
LOCALES ja_JP zh_CN zh_TW
|
||||
PREFIX ${PROJECT_NAME}
|
||||
TARGETS ${PROJECT_NAME}
|
||||
TS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/translations
|
||||
QM_DIR ${CMAKE_CURRENT_BINARY_DIR}/translations
|
||||
)
|
||||
```
|
||||
|
||||
#### Generate Protubuf Source Files
|
||||
```cmake
|
||||
qm_import(Protobuf)
|
||||
|
||||
find_package(Protobuf REQUIRED)
|
||||
qm_create_protobuf(_proto_src
|
||||
INPUT a.proto b.proto
|
||||
INCLUDE_DIRECTORIES src/proto
|
||||
OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/proto
|
||||
)
|
||||
target_sources(${PROJECT_NAME} PUBLIC ${_proto_src})
|
||||
```
|
||||
|
||||
#### Generate Doxygen HTML Documentations
|
||||
```cmake
|
||||
qm_import(Doxygen)
|
||||
|
||||
find_package(Doxygen REQUIRED)
|
||||
qm_setup_doxygen(${PROJECT_NAME}_RunDoxygen
|
||||
NAME ${PROJECT_NAME}
|
||||
DESCRIPTION "my project"
|
||||
MDFILE "${CMAKE_SOURCE_DIR}/README.md"
|
||||
OUTPUT_DIR "${CMAK_BINARY_DIR}/doc"
|
||||
INPUT src
|
||||
TARGETS ${PROJECT_NAME}
|
||||
DEPENDS ${PROJECT_NAME}
|
||||
NO_EXPAND_MACROS
|
||||
Q_OBJECT
|
||||
Q_GADGET
|
||||
Q_DECLARE_TR_FUNCTIONS
|
||||
COMPILE_DEFINITIONS
|
||||
Q_SIGNALS=Q_SIGNALS
|
||||
Q_SLOTS=Q_SLOTS
|
||||
GENERATE_TAGFILE "${PROJECT_NAME}_tagfile.xml"
|
||||
INSTALL_DIR "doc"
|
||||
)
|
||||
```
|
||||
|
||||
### Find Modules
|
||||
|
||||
Use `qm_find_package` to find supported third-party packages.
|
||||
|
||||
+ YY-Thunks: https://github.com/Chuyu-Team/YY-Thunks
|
||||
+ VC-LTL5: https://github.com/Chuyu-Team/VC-LTL5
|
||||
|
||||
### Detailed Documentations
|
||||
|
||||
+ [Core Command](docs/core-command.md)
|
||||
|
||||
The CMake Modules documentations is provided in the comments.
|
||||
|
||||
See `examples` to get detailed use cases.
|
||||
|
||||
## Contributors
|
||||
|
||||
+ [SineStriker](https://github.com/SineStriker)
|
||||
+ [wangwenx190](https://github.com/wangwenx190)
|
||||
+ [RigoLigoRLC](https://github.com/RigoLigoRLC)
|
||||
+ [CrSjimo](https://github.com/CrSjimo)
|
||||
|
||||
## License
|
||||
|
||||
QMSetup is licensed under the MIT License.
|
||||
1176
libs/qwindowkit/qmsetup/cmake/QMSetupAPI.cmake
Normal file
1176
libs/qwindowkit/qmsetup/cmake/QMSetupAPI.cmake
Normal file
File diff suppressed because it is too large
Load Diff
2867
libs/qwindowkit/qmsetup/cmake/doxygen/Doxyfile
Normal file
2867
libs/qwindowkit/qmsetup/cmake/doxygen/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
|
||||
Doxygen Awesome
|
||||
https://github.com/jothepro/doxygen-awesome-css
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 - 2023 jothepro
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
class DoxygenAwesomeDarkModeToggle extends HTMLElement {
|
||||
// SVG icons from https://fonts.google.com/icons
|
||||
// Licensed under the Apache 2.0 license:
|
||||
// https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
static lightModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FCBF00"><rect fill="none" height="24" width="24"/><circle cx="12" cy="12" opacity=".3" r="3"/><path d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg>`
|
||||
static darkModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FE9700"><rect fill="none" height="24" width="24"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27 C17.45,17.19,14.93,19,12,19c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z" opacity=".3"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg>`
|
||||
static title = "Toggle Light/Dark Mode"
|
||||
|
||||
static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode"
|
||||
static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode"
|
||||
|
||||
static _staticConstructor = function() {
|
||||
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.userPreference)
|
||||
// Update the color scheme when the browsers preference changes
|
||||
// without user interaction on the website.
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
||||
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
|
||||
})
|
||||
// Update the color scheme when the tab is made visible again.
|
||||
// It is possible that the appearance was changed in another tab
|
||||
// while this tab was in the background.
|
||||
document.addEventListener("visibilitychange", visibilityState => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
|
||||
}
|
||||
});
|
||||
}()
|
||||
|
||||
static init() {
|
||||
$(function() {
|
||||
$(document).ready(function() {
|
||||
const toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle')
|
||||
toggleButton.title = DoxygenAwesomeDarkModeToggle.title
|
||||
toggleButton.updateIcon()
|
||||
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
||||
toggleButton.updateIcon()
|
||||
})
|
||||
document.addEventListener("visibilitychange", visibilityState => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
toggleButton.updateIcon()
|
||||
}
|
||||
});
|
||||
|
||||
$(document).ready(function(){
|
||||
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
|
||||
})
|
||||
$(window).resize(function(){
|
||||
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.onclick=this.toggleDarkMode
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns `true` for dark-mode, `false` for light-mode system preference
|
||||
*/
|
||||
static get systemPreference() {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns `true` for dark-mode, `false` for light-mode user preference
|
||||
*/
|
||||
static get userPreference() {
|
||||
return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) ||
|
||||
(DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey))
|
||||
}
|
||||
|
||||
static set userPreference(userPreference) {
|
||||
DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference
|
||||
if(!userPreference) {
|
||||
if(DoxygenAwesomeDarkModeToggle.systemPreference) {
|
||||
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true)
|
||||
} else {
|
||||
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)
|
||||
}
|
||||
} else {
|
||||
if(!DoxygenAwesomeDarkModeToggle.systemPreference) {
|
||||
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true)
|
||||
} else {
|
||||
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)
|
||||
}
|
||||
}
|
||||
DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged()
|
||||
}
|
||||
|
||||
static enableDarkMode(enable) {
|
||||
if(enable) {
|
||||
DoxygenAwesomeDarkModeToggle.darkModeEnabled = true
|
||||
document.documentElement.classList.add("dark-mode")
|
||||
document.documentElement.classList.remove("light-mode")
|
||||
} else {
|
||||
DoxygenAwesomeDarkModeToggle.darkModeEnabled = false
|
||||
document.documentElement.classList.remove("dark-mode")
|
||||
document.documentElement.classList.add("light-mode")
|
||||
}
|
||||
}
|
||||
|
||||
static onSystemPreferenceChanged() {
|
||||
DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference
|
||||
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
|
||||
}
|
||||
|
||||
static onUserPreferenceChanged() {
|
||||
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
|
||||
}
|
||||
|
||||
toggleDarkMode() {
|
||||
DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference
|
||||
this.updateIcon()
|
||||
}
|
||||
|
||||
updateIcon() {
|
||||
if(DoxygenAwesomeDarkModeToggle.darkModeEnabled) {
|
||||
this.innerHTML = DoxygenAwesomeDarkModeToggle.darkModeIcon
|
||||
} else {
|
||||
this.innerHTML = DoxygenAwesomeDarkModeToggle.lightModeIcon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle);
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
/**
|
||||
|
||||
Doxygen Awesome
|
||||
https://github.com/jothepro/doxygen-awesome-css
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 - 2023 jothepro
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
|
||||
#MSearchBox {
|
||||
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - var(--searchbar-height) - 1px);
|
||||
}
|
||||
|
||||
#MSearchField {
|
||||
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 66px - var(--searchbar-height));
|
||||
}
|
||||
}
|
||||
2530
libs/qwindowkit/qmsetup/cmake/doxygen/doxygen-awesome.css
Normal file
2530
libs/qwindowkit/qmsetup/cmake/doxygen/doxygen-awesome.css
Normal file
File diff suppressed because it is too large
Load Diff
29
libs/qwindowkit/qmsetup/cmake/doxygen/extra-stylesheet.css
Normal file
29
libs/qwindowkit/qmsetup/cmake/doxygen/extra-stylesheet.css
Normal file
@@ -0,0 +1,29 @@
|
||||
table.memname tr {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.memname td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.memname tr:last-child:not(:first-child) {
|
||||
margin-left: -0.75em
|
||||
}
|
||||
|
||||
html {
|
||||
--external-link-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="%231779c4" d="M10.12,7.5H9.38A.38.38,0,0,0,9,7.88V10.5H1.5V3H4.88a.38.38,0,0,0,.37-.38V1.88a.38.38,0,0,0-.37-.38H1.12A1.12,1.12,0,0,0,0,2.62v8.26A1.12,1.12,0,0,0,1.12,12H9.38a1.12,1.12,0,0,0,1.12-1.12h0v-3A.38.38,0,0,0,10.12,7.5ZM11.44,0h-3A.56.56,0,0,0,8,1l.84.84L3.16,7.51a.56.56,0,0,0,0,.79h0l.54.53a.56.56,0,0,0,.79,0h0L10.2,3.12,11,4a.56.56,0,0,0,1-.4v-3A.56.56,0,0,0,11.44,0Z"/></svg>')
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html:not(.light-mode) {
|
||||
--external-link-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="%231982d2" d="M10.12,7.5H9.38A.38.38,0,0,0,9,7.88V10.5H1.5V3H4.88a.38.38,0,0,0,.37-.38V1.88a.38.38,0,0,0-.37-.38H1.12A1.12,1.12,0,0,0,0,2.62v8.26A1.12,1.12,0,0,0,1.12,12H9.38a1.12,1.12,0,0,0,1.12-1.12h0v-3A.38.38,0,0,0,10.12,7.5ZM11.44,0h-3A.56.56,0,0,0,8,1l.84.84L3.16,7.51a.56.56,0,0,0,0,.79h0l.54.53a.56.56,0,0,0,.79,0h0L10.2,3.12,11,4a.56.56,0,0,0,1-.4v-3A.56.56,0,0,0,11.44,0Z"/></svg>')
|
||||
}
|
||||
}
|
||||
|
||||
a[href^='http://'], a[href^='https://'] {
|
||||
background-image: var(--external-link-icon);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center right;
|
||||
background-size: 0.75em;
|
||||
padding-right: 0.875em;
|
||||
}
|
||||
90
libs/qwindowkit/qmsetup/cmake/doxygen/header.html
Normal file
90
libs/qwindowkit/qmsetup/cmake/doxygen/header.html
Normal file
@@ -0,0 +1,90 @@
|
||||
<!-- HTML header for doxygen 1.9.8-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="$langISO">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=11" />
|
||||
<meta name="generator" content="Doxygen $doxygenversion" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
<title>$projectname: $title</title>
|
||||
<!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME-->
|
||||
<title>$title</title>
|
||||
<!--END !PROJECT_NAME-->
|
||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css" />
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<script type="text/javascript">
|
||||
var page_layout = 1;
|
||||
</script>
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
||||
$treeview $search $mathjax $darkmode
|
||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" /> $extrastylesheet
|
||||
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
|
||||
<script type="text/javascript">
|
||||
DoxygenAwesomeDarkModeToggle.init()
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<div id="side-nav" class="ui-resizable side-nav-resizable">
|
||||
<!-- do not remove this div, it is closed by doxygen! -->
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
|
||||
<div id="top">
|
||||
<!-- do not remove this div, it is closed by doxygen! -->
|
||||
|
||||
<!--BEGIN TITLEAREA-->
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr id="projectrow">
|
||||
<!--BEGIN PROJECT_LOGO-->
|
||||
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo" /></td>
|
||||
<!--END PROJECT_LOGO-->
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
<td id="projectalign">
|
||||
<div id="projectname">$projectname
|
||||
<!--BEGIN PROJECT_NUMBER--><span id="projectnumber"> $projectnumber</span>
|
||||
<!--END PROJECT_NUMBER-->
|
||||
</div>
|
||||
<!--BEGIN PROJECT_BRIEF-->
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
<!--END PROJECT_BRIEF-->
|
||||
</td>
|
||||
<!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME-->
|
||||
<!--BEGIN PROJECT_BRIEF-->
|
||||
<td>
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
</td>
|
||||
<!--END PROJECT_BRIEF-->
|
||||
<!--END !PROJECT_NAME-->
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<!--BEGIN !FULL_SIDEBAR-->
|
||||
<td>$searchbox</td>
|
||||
<!--END !FULL_SIDEBAR-->
|
||||
<!--END SEARCHENGINE-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
</tr>
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<tr>
|
||||
<td colspan="2">$searchbox</td>
|
||||
</tr>
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<!--END SEARCHENGINE-->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--END TITLEAREA-->
|
||||
<!-- end header part -->
|
||||
102
libs/qwindowkit/qmsetup/cmake/find-modules/VC-LTL.cmake
Normal file
102
libs/qwindowkit/qmsetup/cmake/find-modules/VC-LTL.cmake
Normal file
@@ -0,0 +1,102 @@
|
||||
# https://github.com/Chuyu-Team/VC-LTL5
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
if(NOT MSVC OR DEFINED __VC_LTL_CMAKE_INCLUDE_GUARD)
|
||||
return()
|
||||
endif()
|
||||
set(__VC_LTL_CMAKE_INCLUDE_GUARD 1)
|
||||
|
||||
#
|
||||
# VC-LTL自动化加载配置,建议你将此文件单独复制到你的工程再使用,该文件能自动识别当前环境是否存在VC-LTL,并且自动应用。
|
||||
#
|
||||
# 使用方法:
|
||||
# 1. 在“CMakeLists.txt” 添加 “include("VC-LTL helper for cmake.cmake")”。
|
||||
#
|
||||
# VC-LTL默认搜索顺序
|
||||
# 1. “VC-LTL helper for cmake.cmake”所在根目录,即 ${CMAKE_CURRENT_LIST_DIR}
|
||||
# 2. 当前CMake根目录,即 ${CMAKE_CURRENT_SOURCE_DIR}/VC-LTL
|
||||
# 3. 当前项目根目录,即 ${PROJECT_SOURCE_DIR}/VC-LTL
|
||||
# 4. 当前CMake父目录,即 ${CMAKE_CURRENT_SOURCE_DIR}/../VC-LTL
|
||||
# 5. 当前项目根目录,即 ${PROJECT_SOURCE_DIR}/../VC-LTL
|
||||
# 6. 注册表HKEY_CURRENT_USER\Code\VC-LTL@Root
|
||||
#
|
||||
# 把VC-LTL放在其中一个位置即可,VC-LTL就能被自动引用。
|
||||
#
|
||||
# 如果你对默认搜索顺序不满,你可以修改此文件。你也可以直接指定${VC_LTL_Root}宏更加任性的去加载VC-LTL。
|
||||
#
|
||||
|
||||
#####################################################################VC-LTL设置#####################################################################
|
||||
|
||||
#控制最小兼容系统版本,目前可用版本为5.1.2600.0 6.0.6000.0(默认) 6.2.9200.0 10.0.10240.0 10.0.19041.0
|
||||
#注意:VC-LTL依赖YY-Thunks,否则可能无法兼容早期系统。如果需要支持Windows XP,该值必须为5.1.2600.0。
|
||||
if(NOT DEFINED WindowsTargetPlatformMinVersion)
|
||||
set(WindowsTargetPlatformMinVersion "10.0.19041.0" CACHE STRING "" FORCE)
|
||||
endif()
|
||||
|
||||
#VC-LTL使用的CRT模式,SupportLTL可能值为:
|
||||
# * false:禁用VC_LTL
|
||||
# * true:默认值,让VC-LTL自动适应。当最小兼容版本>=10.0时使用ucrt模式,其他系统使用msvcrt模式。
|
||||
# * msvcrt:使用msvcrt.dll作为CRT。注意:msvcrt模式可能不完全支持所有ucrt的新功能。比如setloacl不支持UTF8。
|
||||
# * ucrt:使用ucrtbase.dll作为CRT。注意:早期系统可能需要下载VC-LTL.Redist.Dlls.zip,感谢msvcr14x项目提供兼容XP系统的ucrtbase.dll。
|
||||
#如果需要兼容XP时也使用ucrt,请指定SupportLTL=ucrt。
|
||||
#set(SupportLTL "ucrt")
|
||||
|
||||
#(PR#70 引入),默认关,开启后将使用cmake `INTERFACE`能力,然后单独`target_link_directories(工程名称 VC_LTL)` 即可引用
|
||||
#option(VC_LTL_EnableCMakeInterface "VC_LTL_EnableCMakeInterface" on)
|
||||
####################################################################################################################################################
|
||||
|
||||
if(NOT VC_LTL_Root)
|
||||
if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/_msvcrt.h)
|
||||
set(VC_LTL_Root ${CMAKE_CURRENT_LIST_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT VC_LTL_Root)
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/VC-LTL/_msvcrt.h)
|
||||
set(VC_LTL_Root ${CMAKE_CURRENT_SOURCE_DIR}/VC-LTL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT VC_LTL_Root)
|
||||
if(EXISTS ${PROJECT_SOURCE_DIR}/VC-LTL/_msvcrt.h)
|
||||
set(VC_LTL_Root ${PROJECT_SOURCE_DIR}/VC-LTL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT VC_LTL_Root)
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../VC-LTL/_msvcrt.h)
|
||||
set(VC_LTL_Root ${CMAKE_CURRENT_SOURCE_DIR}/../VC-LTL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT VC_LTL_Root)
|
||||
if(EXISTS ${PROJECT_SOURCE_DIR}/../VC-LTL/_msvcrt.h)
|
||||
set(VC_LTL_Root ${PROJECT_SOURCE_DIR}/../VC-LTL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT VC_LTL_Root)
|
||||
EXECUTE_PROCESS(COMMAND reg query "HKEY_CURRENT_USER\\Code\\VC-LTL" -v "Root"
|
||||
OUTPUT_VARIABLE FOUND_FILE
|
||||
ERROR_VARIABLE ERROR_INFO
|
||||
)
|
||||
|
||||
string(REGEX MATCH "[a-zA-Z]:\\\\.+\\\\"
|
||||
FOUND_LTL
|
||||
${FOUND_FILE})
|
||||
if (NOT ${FOUND_LTL} STREQUAL "")
|
||||
set(VC_LTL_Root ${FOUND_LTL})
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED VC_LTL_Root)
|
||||
string(REGEX MATCH "\\\\\\\\.+\\\\" FOUND_LTL ${FOUND_FILE})
|
||||
if (NOT ${FOUND_LTL} STREQUAL "")
|
||||
set(VC_LTL_Root ${FOUND_LTL})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(VC_LTL_Root)
|
||||
include("${VC_LTL_Root}\\config\\config.cmake")
|
||||
endif()
|
||||
102
libs/qwindowkit/qmsetup/cmake/find-modules/YY-Thunks.cmake
Normal file
102
libs/qwindowkit/qmsetup/cmake/find-modules/YY-Thunks.cmake
Normal file
@@ -0,0 +1,102 @@
|
||||
#[[
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2023 by wangwenx190 (Yuhang Zhao)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
|
||||
# https://github.com/Chuyu-Team/YY-Thunks
|
||||
|
||||
if(NOT MSVC OR DEFINED __YY_THUNKS_CMAKE_INCLUDE_GUARD)
|
||||
return()
|
||||
endif()
|
||||
set(__YY_THUNKS_CMAKE_INCLUDE_GUARD 1)
|
||||
|
||||
if(NOT DEFINED YYTHUNKS_TARGET_OS)
|
||||
set(YYTHUNKS_TARGET_OS "Vista" CACHE STRING "Supported values: Vista, WinXP" FORCE)
|
||||
endif()
|
||||
|
||||
set(__yy_thunks_dir "")
|
||||
if(DEFINED ENV{YYTHUNKS_INSTALL_DIR})
|
||||
set(__env "$ENV{YYTHUNKS_INSTALL_DIR}")
|
||||
if(NOT "x${__env}" STREQUAL "x" AND EXISTS "${__env}")
|
||||
set(__yy_thunks_dir "${__env}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if("x${__yy_thunks_dir}" STREQUAL "x")
|
||||
set(__yy_thunks_reg "")
|
||||
cmake_host_system_information(RESULT __yy_thunks_reg
|
||||
QUERY WINDOWS_REGISTRY "HKCU/Code/YY-Thunks"
|
||||
VALUE "Root")
|
||||
if(NOT "x${__yy_thunks_reg}" STREQUAL "x" AND EXISTS "${__yy_thunks_reg}")
|
||||
set(__yy_thunks_dir "${__yy_thunks_reg}")
|
||||
elseif(EXISTS "${CMAKE_CURRENT_LIST_DIR}/YY-Thunks")
|
||||
set(__yy_thunks_dir "${CMAKE_CURRENT_LIST_DIR}/YY-Thunks")
|
||||
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/YY-Thunks")
|
||||
set(__yy_thunks_dir "${CMAKE_CURRENT_SOURCE_DIR}/YY-Thunks")
|
||||
elseif(EXISTS "${PROJECT_SOURCE_DIR}/YY-Thunks")
|
||||
set(__yy_thunks_dir "${PROJECT_SOURCE_DIR}/YY-Thunks")
|
||||
elseif(EXISTS "${CMAKE_SOURCE_DIR}/YY-Thunks")
|
||||
set(__yy_thunks_dir "${CMAKE_SOURCE_DIR}/YY-Thunks")
|
||||
elseif(EXISTS "${CMAKE_SOURCE_DIR}/../YY-Thunks")
|
||||
set(__yy_thunks_dir "${CMAKE_SOURCE_DIR}/../YY-Thunks")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT "x${__yy_thunks_dir}" STREQUAL "x")
|
||||
cmake_path(NORMAL_PATH __yy_thunks_dir OUTPUT_VARIABLE __yy_thunks_dir)
|
||||
endif()
|
||||
|
||||
if(NOT "x${__yy_thunks_dir}" STREQUAL "x" AND EXISTS "${__yy_thunks_dir}")
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(__yy_thunks_arch x64)
|
||||
else()
|
||||
set(__yy_thunks_arch x86)
|
||||
endif()
|
||||
set(__yy_thunks_obj "${__yy_thunks_dir}/objs/${__yy_thunks_arch}/YY_Thunks_for_${YYTHUNKS_TARGET_OS}.obj")
|
||||
cmake_path(NORMAL_PATH __yy_thunks_obj OUTPUT_VARIABLE __yy_thunks_obj)
|
||||
if(EXISTS "${__yy_thunks_obj}")
|
||||
set(YYTHUNKS_FOUND TRUE CACHE BOOL "" FORCE)
|
||||
set(YYTHUNKS_INSTALL_DIR "${__yy_thunks_dir}" CACHE STRING "" FORCE)
|
||||
set(YYTHUNKS_ARCH "${__yy_thunks_arch}" CACHE STRING "" FORCE)
|
||||
set(YYTHUNKS_OBJ_FILE "YY_Thunks_for_${YYTHUNKS_TARGET_OS}.obj" CACHE STRING "" FORCE)
|
||||
set(YYTHUNKS_OBJ_PATH "${__yy_thunks_obj}" CACHE STRING "" FORCE)
|
||||
add_link_options("${__yy_thunks_obj}")
|
||||
message("###################################################################################################")
|
||||
message("# #")
|
||||
message("# ██ ██ ██ ██ ████████ ██ ██ ██ ██ ███ ██ ██ ██ ███████ #")
|
||||
message("# ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ #")
|
||||
message("# ████ ████ █████ ██ ███████ ██ ██ ██ ██ ██ █████ ███████ #")
|
||||
message("# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ #")
|
||||
message("# ██ ██ ██ ██ ██ ██████ ██ ████ ██ ██ ███████ #")
|
||||
message("# #")
|
||||
message("###################################################################################################")
|
||||
message("")
|
||||
message(" YY-Thunks install dir :" ${__yy_thunks_dir})
|
||||
message(" YY-Thunks target platform :" ${YYTHUNKS_TARGET_OS})
|
||||
message(" YY-Thunks obj file path :" ${__yy_thunks_obj})
|
||||
message("")
|
||||
else()
|
||||
message(WARNING "YY-Thunks's obj file is missing!")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "Can't locate the YY-Thunks installation directory!")
|
||||
endif()
|
||||
238
libs/qwindowkit/qmsetup/cmake/modules/CompilerOptions.cmake
Normal file
238
libs/qwindowkit/qmsetup/cmake/modules/CompilerOptions.cmake
Normal file
@@ -0,0 +1,238 @@
|
||||
include_guard(DIRECTORY)
|
||||
|
||||
#[[
|
||||
Disable all possible warnings from the compiler.
|
||||
|
||||
qm_compiler_no_warnings()
|
||||
]] #
|
||||
macro(qm_compiler_no_warnings)
|
||||
foreach(__lang C CXX)
|
||||
if(NOT "x${CMAKE_${__lang}_FLAGS}" STREQUAL "x")
|
||||
if(MSVC)
|
||||
string(REGEX REPLACE " [/-]W[01234] " " " CMAKE_${__lang}_FLAGS ${CMAKE_${__lang}_FLAGS})
|
||||
else()
|
||||
string(REGEX REPLACE " -W(all)?(extra)? " " " CMAKE_${__lang}_FLAGS ${CMAKE_${__lang}_FLAGS})
|
||||
string(REGEX REPLACE " -[W]?pedantic " " " CMAKE_${__lang}_FLAGS ${CMAKE_${__lang}_FLAGS})
|
||||
endif()
|
||||
endif()
|
||||
string(APPEND CMAKE_${__lang}_FLAGS " -w ")
|
||||
endforeach()
|
||||
if(MSVC)
|
||||
add_compile_definitions(-D_CRT_NON_CONFORMING_SWPRINTFS)
|
||||
add_compile_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE)
|
||||
add_compile_definitions(-D_CRT_NONSTDC_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE)
|
||||
add_compile_definitions(-D_SCL_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_DEPRECATE)
|
||||
add_compile_definitions(-D_SILENCE_ALL_MS_EXT_DEPRECATION_WARNINGS)
|
||||
else()
|
||||
foreach(__lang C CXX)
|
||||
string(APPEND CMAKE_${__lang}_FLAGS " -fpermissive ")
|
||||
endforeach()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
#[[
|
||||
Enable all possible warnings from the compiler.
|
||||
|
||||
qm_compiler_max_warnings()
|
||||
]] #
|
||||
function(qm_compiler_max_warnings)
|
||||
if(MSVC)
|
||||
add_compile_options(-W4)
|
||||
elseif("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xClang")
|
||||
add_compile_options(-Weverything)
|
||||
else()
|
||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
#[[
|
||||
Treat all warnings as errors.
|
||||
|
||||
qm_compiler_warnings_are_errors()
|
||||
]] #
|
||||
function(qm_compiler_warnings_are_errors)
|
||||
if(MSVC)
|
||||
add_compile_options(-WX)
|
||||
else()
|
||||
add_compile_options(-Werror)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
#[[
|
||||
Prevent the compiler from receiving unknown parameters.
|
||||
|
||||
qm_compiler_no_unknown_options()
|
||||
]] #
|
||||
function(qm_compiler_no_unknown_options)
|
||||
if(MSVC)
|
||||
if(MSVC_VERSION GREATER_EQUAL 1930) # Visual Studio 2022 version 17.0
|
||||
add_compile_options(-options:strict)
|
||||
endif()
|
||||
add_link_options(-WX)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
#[[
|
||||
Remove all unused code from the final binary.
|
||||
|
||||
qm_compiler_eliminate_dead_code()
|
||||
]] #
|
||||
function(qm_compiler_eliminate_dead_code)
|
||||
if(MSVC)
|
||||
add_compile_options(-Gw -Gy -Zc:inline)
|
||||
add_link_options(-OPT:REF -OPT:ICF -OPT:LBR)
|
||||
else()
|
||||
add_compile_options(-ffunction-sections -fdata-sections)
|
||||
if(APPLE)
|
||||
add_link_options(-Wl,-dead_strip)
|
||||
else()
|
||||
add_link_options(-Wl,--as-needed -Wl,--strip-all -Wl,--gc-sections)
|
||||
endif()
|
||||
if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xClang")
|
||||
add_link_options(-Wl,--icf=all)
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
#[[
|
||||
Only export symbols which are marked to be exported, just like MSVC.
|
||||
|
||||
qm_compiler_dont_export_by_default()
|
||||
]] #
|
||||
macro(qm_compiler_dont_export_by_default)
|
||||
set(CMAKE_C_VISIBILITY_PRESET "hidden")
|
||||
set(CMAKE_CXX_VISIBILITY_PRESET "hidden")
|
||||
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
|
||||
endmacro()
|
||||
|
||||
#[[
|
||||
Enable all possible security issue mitigations from your compiler.
|
||||
|
||||
qm_compiler_enable_secure_code()
|
||||
]] #
|
||||
macro(qm_compiler_enable_secure_code)
|
||||
if(MSVC)
|
||||
add_compile_options(-GS -sdl -guard:cf)
|
||||
add_link_options(-DYNAMICBASE -FIXED:NO -NXCOMPAT -GUARD:CF)
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
add_link_options(-SAFESEH)
|
||||
endif()
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
add_link_options(-HIGHENTROPYVA)
|
||||
endif()
|
||||
if(MSVC_VERSION GREATER_EQUAL 1920) # Visual Studio 2019 version 16.0
|
||||
add_link_options(-CETCOMPAT)
|
||||
endif()
|
||||
if(MSVC_VERSION GREATER_EQUAL 1925) # Visual Studio 2019 version 16.5
|
||||
add_compile_options(-Qspectre-load)
|
||||
elseif(MSVC_VERSION GREATER_EQUAL 1912) # Visual Studio 2017 version 15.5
|
||||
add_compile_options(-Qspectre)
|
||||
endif()
|
||||
if(MSVC_VERSION GREATER_EQUAL 1927) # Visual Studio 2019 version 16.7
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
add_compile_options(-guard:ehcont)
|
||||
add_link_options(-guard:ehcont)
|
||||
endif()
|
||||
endif()
|
||||
if(MSVC_VERSION GREATER_EQUAL 1930) # Visual Studio 2022 version 17.0
|
||||
add_compile_options(-Qspectre-jmp)
|
||||
endif()
|
||||
elseif(MINGW)
|
||||
if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xClang")
|
||||
add_compile_options(-mguard=cf)
|
||||
add_link_options(-mguard=cf)
|
||||
else()
|
||||
endif()
|
||||
else()
|
||||
add_compile_options(-mshstk -ftrivial-auto-var-init=pattern
|
||||
-fstack-protector-strong -fstack-clash-protection
|
||||
-fcf-protection=full)
|
||||
add_link_options(-Wl,-z,relro,-z,now)
|
||||
if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xClang")
|
||||
add_compile_options(-mretpoline -mspeculative-load-hardening)
|
||||
if(NOT APPLE)
|
||||
add_compile_options(-fsanitize=cfi -fsanitize-cfi-cross-dso)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
#[[
|
||||
Enable all possible Qt coding style policies.
|
||||
|
||||
qm_compiler_enable_strict_qt(
|
||||
TARGETS <target1> <target2> ... <targetN>
|
||||
[NO_DEPRECATED_API]
|
||||
[ALLOW_KEYWORD]
|
||||
[ALLOW_UNSAFE_FLAGS]
|
||||
)
|
||||
|
||||
NO_DEPRECATED_API: Disable the use of any deprecated Qt APIs. Only has effect since Qt6.
|
||||
ALLOW_KEYWORD: Allow the use of the traditional Qt keywords such as "signal" "slot" "emit".
|
||||
ALLOW_UNSAFE_FLAGS: Allow the use of the unsafe QFlags (unsafe: can be implicitly cast to and from "int").
|
||||
]] #
|
||||
function(qm_compiler_enable_strict_qt)
|
||||
cmake_parse_arguments(arg "NO_DEPRECATED_API;ALLOW_KEYWORD;ALLOW_UNSAFE_FLAGS" "" "TARGETS" ${ARGN})
|
||||
if(NOT arg_TARGETS)
|
||||
message(AUTHOR_WARNING "qm_compiler_enable_strict_qt: you need to specify at least one target!")
|
||||
return()
|
||||
endif()
|
||||
if(arg_UNPARSED_ARGUMENTS)
|
||||
message(AUTHOR_WARNING "qm_compiler_enable_strict_qt: Unrecognized arguments: ${arg_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
foreach(_target IN LISTS arg_TARGETS)
|
||||
if(NOT TARGET ${_target})
|
||||
message(AUTHOR_WARNING "qm_compiler_enable_strict_qt: ${_target} is not a valid CMake target!")
|
||||
continue()
|
||||
endif()
|
||||
target_compile_definitions(${_target} PRIVATE
|
||||
QT_NO_CAST_TO_ASCII
|
||||
QT_NO_CAST_FROM_ASCII
|
||||
QT_NO_CAST_FROM_BYTEARRAY
|
||||
QT_NO_URL_CAST_FROM_STRING
|
||||
QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
|
||||
QT_NO_JAVA_STYLE_ITERATORS
|
||||
QT_NO_FOREACH QT_NO_QFOREACH
|
||||
QT_NO_AS_CONST QT_NO_QASCONST
|
||||
QT_NO_EXCHANGE QT_NO_QEXCHANGE
|
||||
QT_NO_QPAIR
|
||||
QT_NO_INTEGRAL_STRINGS
|
||||
QT_NO_USING_NAMESPACE
|
||||
QT_NO_CONTEXTLESS_CONNECT
|
||||
QT_EXPLICIT_QFILE_CONSTRUCTION_FROM_PATH
|
||||
QT_USE_NODISCARD_FILE_OPEN
|
||||
QT_USE_QSTRINGBUILDER
|
||||
QT_USE_FAST_OPERATOR_PLUS
|
||||
QT_DEPRECATED_WARNINGS # Have no effect since 5.13
|
||||
QT_DEPRECATED_WARNINGS_SINCE=0x0A0000 # Deprecated since 6.5
|
||||
QT_WARN_DEPRECATED_UP_TO=0x0A0000 # Available since 6.5
|
||||
)
|
||||
if(arg_NO_DEPRECATED_API)
|
||||
target_compile_definitions(${_target} PRIVATE
|
||||
QT_DISABLE_DEPRECATED_BEFORE=0x0A0000 # Deprecated since 6.5
|
||||
QT_DISABLE_DEPRECATED_UP_TO=0x0A0000 # Available since 6.5
|
||||
)
|
||||
endif()
|
||||
# On Windows enabling this flag requires us re-compile Qt with this flag enabled,
|
||||
# so only enable it on non-Windows platforms.
|
||||
if(NOT WIN32)
|
||||
target_compile_definitions(${_target} PRIVATE
|
||||
QT_STRICT_ITERATORS
|
||||
)
|
||||
endif()
|
||||
# We handle this flag specially because some Qt headers may still use the
|
||||
# traditional keywords (especially some private headers).
|
||||
if(NOT arg_ALLOW_KEYWORD)
|
||||
target_compile_definitions(${_target} PRIVATE
|
||||
QT_NO_KEYWORDS
|
||||
)
|
||||
endif()
|
||||
# We handle this flag specially because some Qt headers may still use the
|
||||
# unsafe flags (especially some QtQuick headers).
|
||||
if(NOT arg_ALLOW_UNSAFE_FLAGS)
|
||||
target_compile_definitions(${_target} PRIVATE
|
||||
QT_TYPESAFE_FLAGS
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
421
libs/qwindowkit/qmsetup/cmake/modules/Deploy.cmake
Normal file
421
libs/qwindowkit/qmsetup/cmake/modules/Deploy.cmake
Normal file
@@ -0,0 +1,421 @@
|
||||
#[[
|
||||
Warning: This module depends on `qmcorecmd` after installation.
|
||||
]] #
|
||||
if(NOT QMSETUP_CORECMD_EXECUTABLE)
|
||||
message(FATAL_ERROR "QMSETUP_CORECMD_EXECUTABLE not defined. Add find_package(qmsetup) to CMake first.")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED QMSETUP_APPLOCAL_DEPS_PATHS)
|
||||
set(QMSETUP_APPLOCAL_DEPS_PATHS)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED QMSETUP_APPLOCAL_DEPS_PATHS_DEBUG)
|
||||
set(QMSETUP_APPLOCAL_DEPS_PATHS_DEBUG ${QMSETUP_APPLOCAL_DEPS_PATHS})
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED QMSETUP_APPLOCAL_DEPS_PATHS_RELEASE)
|
||||
set(QMSETUP_APPLOCAL_DEPS_PATHS_RELEASE ${QMSETUP_APPLOCAL_DEPS_PATHS})
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED QMSETUP_APPLOCAL_DEPS_PATHS_RELWITHDEBINFO)
|
||||
set(QMSETUP_APPLOCAL_DEPS_PATHS_RELWITHDEBINFO ${QMSETUP_APPLOCAL_DEPS_PATHS_RELEASE})
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED QMSETUP_APPLOCAL_DEPS_PATHS_MINSIZEREL)
|
||||
set(QMSETUP_APPLOCAL_DEPS_PATHS_MINSIZEREL ${QMSETUP_APPLOCAL_DEPS_PATHS_RELEASE})
|
||||
endif()
|
||||
|
||||
include_guard(DIRECTORY)
|
||||
|
||||
#[[
|
||||
Record searching paths for Windows Executables, must be called before calling `qm_win_applocal_deps`
|
||||
or `qm_deploy_directory` if your project supports Windows.
|
||||
|
||||
WARNING: This function is deprecated.
|
||||
|
||||
qm_win_record_deps(<target>)
|
||||
]] #
|
||||
function(qm_win_record_deps _target)
|
||||
if(NOT WIN32)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(_paths)
|
||||
get_target_property(_link_libraries ${_target} LINK_LIBRARIES)
|
||||
|
||||
foreach(_item IN LISTS _link_libraries)
|
||||
if(NOT TARGET ${_item})
|
||||
continue()
|
||||
endif()
|
||||
|
||||
get_target_property(_imported ${_item} IMPORTED)
|
||||
|
||||
if(_imported)
|
||||
get_target_property(_path ${_item} LOCATION)
|
||||
|
||||
if(NOT _path OR NOT ${_path} MATCHES "\\.dll$")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
set(_path "$<TARGET_PROPERTY:${_item},LOCATION_$<CONFIG>>")
|
||||
else()
|
||||
get_target_property(_type ${_item} TYPE)
|
||||
|
||||
if(NOT ${_type} MATCHES "SHARED_LIBRARY")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
set(_path "$<TARGET_FILE:${_item}>")
|
||||
endif()
|
||||
|
||||
list(APPEND _paths ${_path})
|
||||
endforeach()
|
||||
|
||||
if(NOT _paths)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(_deps_file "${CMAKE_CURRENT_BINARY_DIR}/${_target}_deps_$<CONFIG>.txt")
|
||||
file(GENERATE OUTPUT ${_deps_file} CONTENT "$<JOIN:${_paths},\n>")
|
||||
set_target_properties(${_target} PROPERTIES QMSETUP_DEPENDENCIES_FILE ${_deps_file})
|
||||
endfunction()
|
||||
|
||||
#[[
|
||||
Automatically copy dependencies for Windows Executables after build.
|
||||
|
||||
qm_win_applocal_deps(<target>
|
||||
[CUSTOM_TARGET <target>]
|
||||
[FORCE] [VERBOSE]
|
||||
[EXTRA_SEARCHING_PATHS <path...>]
|
||||
[EXTRA_TARGETS <target...>]
|
||||
[OUTPUT_DIR <dir>]
|
||||
[EXCLUDE <pattern...>]
|
||||
)
|
||||
]] #
|
||||
function(qm_win_applocal_deps _target)
|
||||
if(NOT WIN32)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(options FORCE VERBOSE)
|
||||
set(oneValueArgs TARGET CUSTOM_TARGET OUTPUT_DIR)
|
||||
set(multiValueArgs EXTRA_SEARCHING_PATHS EXTRA_TARGETS EXCLUDE)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# Get output directory and deploy target
|
||||
set(_out_dir)
|
||||
set(_deploy_target)
|
||||
|
||||
if(FUNC_CUSTOM_TARGET)
|
||||
set(_deploy_target ${FUNC_CUSTOM_TARGET})
|
||||
|
||||
if(NOT TARGET ${_deploy_target})
|
||||
add_custom_target(${_deploy_target})
|
||||
endif()
|
||||
else()
|
||||
set(_deploy_target ${_target})
|
||||
endif()
|
||||
|
||||
if(FUNC_OUTPUT_DIR)
|
||||
set(_out_dir ${FUNC_OUTPUT_DIR})
|
||||
else()
|
||||
set(_out_dir "$<TARGET_FILE_DIR:${_target}>")
|
||||
endif()
|
||||
|
||||
if(NOT _out_dir)
|
||||
message(FATAL_ERROR "qm_win_applocal_deps: cannot determine output directory.")
|
||||
endif()
|
||||
|
||||
# Get dep files
|
||||
set(_dep_files)
|
||||
_qm_win_get_all_dep_files(_dep_files ${_target})
|
||||
|
||||
foreach(_item IN LISTS FUNC_EXTRA_TARGETS)
|
||||
_qm_win_get_all_dep_files(_dep_files ${_item})
|
||||
endforeach()
|
||||
|
||||
# Prepare command
|
||||
set(_args)
|
||||
|
||||
if(FUNC_FORCE)
|
||||
list(APPEND _args -f)
|
||||
endif()
|
||||
|
||||
if(FUNC_VERBOSE)
|
||||
list(APPEND _args -V)
|
||||
endif()
|
||||
|
||||
# Add extra searching paths
|
||||
foreach(_item IN LISTS FUNC_EXTRA_SEARCHING_PATHS)
|
||||
list(APPEND _args "-L${_item}")
|
||||
endforeach()
|
||||
|
||||
# Add global extra searching paths
|
||||
if(CMAKE_BUILD_TYPE)
|
||||
string(TOUPPER ${CMAKE_BUILD_TYPE} _build_type_upper)
|
||||
|
||||
if(QMSETUP_APPLOCAL_DEPS_PATHS_${_build_type_upper})
|
||||
foreach(_item IN LISTS QMSETUP_APPLOCAL_DEPS_PATHS_${_build_type_upper})
|
||||
get_filename_component(_item ${_item} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
|
||||
list(APPEND _args "-L${_item}")
|
||||
endforeach()
|
||||
elseif(QMSETUP_APPLOCAL_DEPS_PATHS)
|
||||
foreach(_item IN LISTS QMSETUP_APPLOCAL_DEPS_PATHS)
|
||||
get_filename_component(_item ${_item} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
|
||||
list(APPEND _args "-L${_item}")
|
||||
endforeach()
|
||||
endif()
|
||||
else()
|
||||
foreach(_item IN LISTS QMSETUP_APPLOCAL_DEPS_PATHS)
|
||||
get_filename_component(_item ${_item} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
|
||||
list(APPEND _args "-L${_item}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
foreach(_item IN LISTS _dep_files)
|
||||
list(APPEND _args "--linkdirs-file" "${_item}")
|
||||
endforeach()
|
||||
|
||||
foreach(_item IN LISTS FUNC_EXCLUDE)
|
||||
list(APPEND _args -e ${_item})
|
||||
endforeach()
|
||||
|
||||
list(APPEND _args "$<TARGET_FILE:${_target}>")
|
||||
|
||||
add_custom_command(TARGET ${_deploy_target} POST_BUILD
|
||||
COMMAND ${QMSETUP_CORECMD_EXECUTABLE} deploy ${_args}
|
||||
WORKING_DIRECTORY ${_out_dir}
|
||||
VERBATIM
|
||||
)
|
||||
endfunction()
|
||||
|
||||
#[[
|
||||
Add deploy command when install project, not available in debug mode.
|
||||
|
||||
qm_deploy_directory(<install_dir>
|
||||
[FORCE] [STANDARD] [VERBOSE]
|
||||
[LIBRARY_DIR <dir>]
|
||||
[EXTRA_LIBRARIES <path>...]
|
||||
[EXTRA_PLUGIN_PATHS <path>...]
|
||||
[EXTRA_SEARCHING_PATHS <path>...]
|
||||
|
||||
[PLUGINS <plugin>...]
|
||||
[PLUGIN_DIR <dir>]
|
||||
|
||||
[QML <qml>...]
|
||||
[QML_DIR <dir>]
|
||||
|
||||
[WIN_TARGETS <target>...]
|
||||
|
||||
[COMMENT <comment>]
|
||||
)
|
||||
|
||||
PLUGINS: Qt plugins, in format of `<category>/<name>`
|
||||
PLUGIN_DIR: Qt plugins destination
|
||||
EXTRA_PLUGIN_PATHS: Extra Qt plugins searching paths
|
||||
QML: Qt qml directories
|
||||
QML_DIR: Qt qml destination
|
||||
LIBRARY_DIR: Library destination
|
||||
EXTRA_LIBRARIES: Extra library names list to deploy
|
||||
EXTRA_SEARCHING_PATHS: Extra library searching paths
|
||||
]] #
|
||||
function(qm_deploy_directory _install_dir)
|
||||
set(options FORCE STANDARD VERBOSE)
|
||||
set(oneValueArgs LIBRARY_DIR PLUGIN_DIR QML_DIR COMMENT)
|
||||
set(multiValueArgs EXTRA_PLUGIN_PATHS PLUGINS QML WIN_TARGETS EXTRA_SEARCHING_PATHS EXTRA_LIBRARIES)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# Get qmake
|
||||
if((FUNC_PLUGINS OR FUNC_QML) AND NOT DEFINED QT_QMAKE_EXECUTABLE)
|
||||
if(TARGET Qt${QT_VERSION_MAJOR}::qmake)
|
||||
qm_get_executable_location(Qt${QT_VERSION_MAJOR}::qmake _qmake_path)
|
||||
set(QT_QMAKE_EXECUTABLE "${_qmake_path}" CACHE FILEPATH "Path to qmake executable" FORCE)
|
||||
elseif((FUNC_PLUGINS AND NOT FUNC_EXTRA_PLUGIN_PATHS) OR FUNC_QML)
|
||||
message(FATAL_ERROR "qm_deploy_directory: qmake not defined. Add find_package(Qt5 COMPONENTS Core) to CMake to enable.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Set values
|
||||
qm_set_value(_lib_dir FUNC_LIBRARY_DIR "${_install_dir}/${QMSETUP_SHARED_LIBRARY_CATEGORY}")
|
||||
qm_set_value(_plugin_dir FUNC_PLUGIN_DIR "${_install_dir}/plugins")
|
||||
qm_set_value(_qml_dir FUNC_QML_DIR "${_install_dir}/qml")
|
||||
|
||||
# Prepare commands
|
||||
set(_args
|
||||
-i "${_install_dir}"
|
||||
-m "${QMSETUP_CORECMD_EXECUTABLE}"
|
||||
--plugindir "${_plugin_dir}"
|
||||
--libdir "${_lib_dir}"
|
||||
--qmldir "${_qml_dir}"
|
||||
)
|
||||
set(_searching_paths)
|
||||
|
||||
if(QT_QMAKE_EXECUTABLE)
|
||||
list(APPEND _args --qmake "${QT_QMAKE_EXECUTABLE}")
|
||||
endif()
|
||||
|
||||
# Add Qt plugins
|
||||
foreach(_item IN LISTS FUNC_PLUGINS)
|
||||
list(APPEND _args --plugin "${_item}")
|
||||
endforeach()
|
||||
|
||||
# Add QML modules
|
||||
foreach(_item IN LISTS FUNC_QML)
|
||||
list(APPEND _args --qml "${_item}")
|
||||
endforeach()
|
||||
|
||||
# Add extra plugin paths
|
||||
foreach(_item IN LISTS FUNC_EXTRA_PLUGIN_PATHS)
|
||||
list(APPEND _args --extra "${_item}")
|
||||
endforeach()
|
||||
|
||||
# Add extra searching paths
|
||||
foreach(_item IN LISTS FUNC_EXTRA_SEARCHING_PATHS)
|
||||
get_filename_component(_item ${_item} ABSOLUTE)
|
||||
list(APPEND _searching_paths ${_item})
|
||||
endforeach()
|
||||
|
||||
# Add global extra searching paths
|
||||
if(CMAKE_BUILD_TYPE)
|
||||
string(TOUPPER ${CMAKE_BUILD_TYPE} _build_type_upper)
|
||||
|
||||
if(QMSETUP_APPLOCAL_DEPS_PATHS_${_build_type_upper})
|
||||
foreach(_item IN LISTS QMSETUP_APPLOCAL_DEPS_PATHS_${_build_type_upper})
|
||||
get_filename_component(_item ${_item} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
|
||||
list(APPEND _searching_paths ${_item})
|
||||
endforeach()
|
||||
elseif(QMSETUP_APPLOCAL_DEPS_PATHS)
|
||||
foreach(_item IN LISTS QMSETUP_APPLOCAL_DEPS_PATHS)
|
||||
get_filename_component(_item ${_item} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
|
||||
list(APPEND _searching_paths ${_item})
|
||||
endforeach()
|
||||
endif()
|
||||
else()
|
||||
foreach(_item IN LISTS QMSETUP_APPLOCAL_DEPS_PATHS)
|
||||
get_filename_component(_item ${_item} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
|
||||
list(APPEND _searching_paths ${_item})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
foreach(_item IN LISTS _searching_paths)
|
||||
list(APPEND _args -L "${_item}")
|
||||
endforeach()
|
||||
|
||||
if(WIN32)
|
||||
set(_dep_files)
|
||||
|
||||
if(FUNC_WIN_TARGETS)
|
||||
_qm_win_get_all_dep_files(_dep_files ${FUNC_WIN_TARGETS})
|
||||
endif()
|
||||
|
||||
foreach(_item IN LISTS _dep_files)
|
||||
list(APPEND _args "--linkdirs-file" "${_item}")
|
||||
endforeach()
|
||||
|
||||
set(_script_quoted "cmd /c \"${QMSETUP_MODULES_DIR}/scripts/windeps.bat\"")
|
||||
else()
|
||||
set(_script_quoted "bash \"${QMSETUP_MODULES_DIR}/scripts/unixdeps.sh\"")
|
||||
endif()
|
||||
|
||||
# Add extra libraries
|
||||
foreach(_item IN LISTS _searching_paths)
|
||||
foreach(_lib IN LISTS FUNC_EXTRA_LIBRARIES)
|
||||
set(_path "${_item}/${_lib}")
|
||||
|
||||
if((EXISTS ${_path}) AND(NOT IS_DIRECTORY ${_path}))
|
||||
list(APPEND _args --copy ${_path} ${_lib_dir})
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
# Add options
|
||||
if(FUNC_FORCE)
|
||||
list(APPEND _args "-f")
|
||||
endif()
|
||||
|
||||
if(FUNC_STANDARD)
|
||||
list(APPEND _args "-s")
|
||||
endif()
|
||||
|
||||
if(FUNC_VERBOSE)
|
||||
list(APPEND _args "-V")
|
||||
endif()
|
||||
|
||||
set(_args_quoted)
|
||||
|
||||
foreach(_item IN LISTS _args)
|
||||
set(_args_quoted "${_args_quoted}\"${_item}\" ")
|
||||
endforeach()
|
||||
|
||||
set(_comment_code)
|
||||
|
||||
if(FUNC_COMMENT)
|
||||
set(_comment_code "message(STATUS \"${FUNC_COMMENT}\")")
|
||||
endif()
|
||||
|
||||
# Add install command
|
||||
install(CODE "
|
||||
${_comment_code}
|
||||
execute_process(
|
||||
COMMAND ${_script_quoted} ${_args_quoted}
|
||||
WORKING_DIRECTORY \"${_install_dir}\"
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
")
|
||||
endfunction()
|
||||
|
||||
# ----------------------------------
|
||||
# Private functions
|
||||
# ----------------------------------
|
||||
function(_qm_win_get_all_dep_files _out)
|
||||
# Get searching paths
|
||||
macro(get_recursive_dynamic_dependencies _current_target _result)
|
||||
get_target_property(_deps ${_current_target} LINK_LIBRARIES)
|
||||
|
||||
if(_deps)
|
||||
foreach(_dep IN LISTS _deps)
|
||||
if(NOT TARGET ${_dep})
|
||||
continue()
|
||||
endif()
|
||||
|
||||
get_target_property(_type ${_dep} TYPE)
|
||||
|
||||
if(_type STREQUAL "SHARED_LIBRARY")
|
||||
list(APPEND ${_result} ${_dep})
|
||||
endif()
|
||||
|
||||
get_recursive_dynamic_dependencies(${_dep} ${_result})
|
||||
endforeach()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
set(_visited_targets ${ARGN})
|
||||
|
||||
foreach(_target ${ARGN})
|
||||
set(_all_deps)
|
||||
get_recursive_dynamic_dependencies(${_target} _all_deps)
|
||||
|
||||
foreach(_cur_dep IN LISTS _all_deps)
|
||||
if(${_cur_dep} IN_LIST _visited_targets)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
list(APPEND _visited_targets ${_cur_dep})
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
set(_dep_files)
|
||||
|
||||
foreach(_target IN LISTS _visited_targets)
|
||||
# Add file
|
||||
get_target_property(_file ${_target} QMSETUP_DEPENDENCIES_FILE)
|
||||
|
||||
if(NOT _file)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
list(APPEND _dep_files ${_file})
|
||||
endforeach()
|
||||
|
||||
set(${_out} ${_dep_files} PARENT_SCOPE)
|
||||
endfunction()
|
||||
235
libs/qwindowkit/qmsetup/cmake/modules/Doxygen.cmake
Normal file
235
libs/qwindowkit/qmsetup/cmake/modules/Doxygen.cmake
Normal file
@@ -0,0 +1,235 @@
|
||||
include_guard(DIRECTORY)
|
||||
|
||||
#[[
|
||||
Add Doxygen documentation generating target.
|
||||
|
||||
qm_setup_doxygen(<target>
|
||||
[NAME <name>]
|
||||
[VERSION <version>]
|
||||
[DESCRIPTION <desc>]
|
||||
[LOGO <file>]
|
||||
[MDFILE <file>]
|
||||
[OUTPUT_DIR <dir>]
|
||||
[INSTALL_DIR <dir>]
|
||||
|
||||
[TAGFILES <file> ...]
|
||||
[GENERATE_TAGFILE <file>]
|
||||
|
||||
[INPUT <file> ...]
|
||||
[INCLUDE_DIRECTORIES <dir> ...]
|
||||
[COMPILE_DEFINITIONS <NAME=VALUE> ...]
|
||||
[TARGETS <target> ...]
|
||||
[ENVIRONMENT_EXPORTS <key> ...]
|
||||
[NO_EXPAND_MACROS <macro> ...]
|
||||
[DEPENDS <dependency> ...]
|
||||
)
|
||||
]] #
|
||||
function(qm_setup_doxygen _target)
|
||||
set(options)
|
||||
set(oneValueArgs NAME VERSION DESCRIPTION LOGO MDFILE OUTPUT_DIR INSTALL_DIR GENERATE_TAGFILE)
|
||||
set(multiValueArgs INPUT TAGFILES INCLUDE_DIRECTORIES COMPILE_DEFINITIONS TARGETS ENVIRONMENT_EXPORTS
|
||||
NO_EXPAND_MACROS DEPENDS
|
||||
)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT DOXYGEN_EXECUTABLE)
|
||||
message(FATAL_ERROR "qm_setup_doxygen: DOXYGEN_EXECUTABLE not defined. Add find_package(Doxygen) to CMake to enable.")
|
||||
endif()
|
||||
|
||||
set(DOXYGEN_FILE_DIR ${QMSETUP_MODULES_DIR}/doxygen)
|
||||
|
||||
if(FUNC_NAME)
|
||||
set(_name ${FUNC_NAME})
|
||||
else()
|
||||
set(_name ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
if(FUNC_VERSION)
|
||||
set(_version ${FUNC_VERSION})
|
||||
else()
|
||||
set(_version ${PROJECT_VERSION})
|
||||
endif()
|
||||
|
||||
if(FUNC_DESCRIPTION)
|
||||
set(_desc ${FUNC_DESCRIPTION})
|
||||
elseif(PROJECT_DESCRIPTION)
|
||||
set(_desc ${PROJECT_DESCRIPTION})
|
||||
else()
|
||||
set(_desc ${_name})
|
||||
endif()
|
||||
|
||||
if(FUNC_LOGO)
|
||||
set(_logo ${FUNC_LOGO})
|
||||
else()
|
||||
set(_logo)
|
||||
endif()
|
||||
|
||||
if(FUNC_MDFILE)
|
||||
set(_mdfile ${FUNC_MDFILE})
|
||||
else()
|
||||
set(_mdfile)
|
||||
endif()
|
||||
|
||||
if(FUNC_GENERATE_TAGFILE)
|
||||
set(_tagfile ${FUNC_GENERATE_TAGFILE})
|
||||
else()
|
||||
set(_tagfile)
|
||||
endif()
|
||||
|
||||
set(_sep " \\\n ")
|
||||
|
||||
# Generate include file
|
||||
set(_doxy_includes "${CMAKE_CURRENT_BINARY_DIR}/cmake/doxygen_${_target}_$<CONFIG>.inc")
|
||||
set(_doxy_output_dir "${CMAKE_CURRENT_BINARY_DIR}/doxygen_${_target}")
|
||||
|
||||
set(_input "")
|
||||
set(_tagfiles "")
|
||||
set(_includes "")
|
||||
set(_defines "")
|
||||
set(_no_expand "")
|
||||
|
||||
if(FUNC_INPUT)
|
||||
set(_input "INPUT = $<JOIN:${FUNC_INPUT},${_sep}>\n\n")
|
||||
else()
|
||||
set(_input "INPUT = \n\n")
|
||||
endif()
|
||||
|
||||
if(FUNC_TAGFILES)
|
||||
set(_tagfiles "TAGFILES = $<JOIN:${FUNC_TAGFILES},${_sep}>\n\n")
|
||||
else()
|
||||
set(_tagfiles "TAGFILES = \n\n")
|
||||
endif()
|
||||
|
||||
if(FUNC_INCLUDE_DIRECTORIES)
|
||||
set(_includes "INCLUDE_PATH = $<JOIN:${FUNC_INCLUDE_DIRECTORIES},${_sep}>\n\n")
|
||||
else()
|
||||
set(_includes "INCLUDE_PATH = \n\n")
|
||||
endif()
|
||||
|
||||
if(FUNC_COMPILE_DEFINITIONS)
|
||||
set(_defines "PREDEFINED = $<JOIN:${FUNC_COMPILE_DEFINITIONS},${_sep}>\n\n")
|
||||
else()
|
||||
set(_defines "PREDEFINED = \n\n")
|
||||
endif()
|
||||
|
||||
if(FUNC_NO_EXPAND_MACROS)
|
||||
set(_temp_list)
|
||||
|
||||
foreach(_item IN LISTS FUNC_NO_EXPAND_MACROS)
|
||||
list(APPEND _temp_list "${_item}=")
|
||||
endforeach()
|
||||
|
||||
set(_no_expand "PREDEFINED += $<JOIN:${_temp_list},${_sep}>\n\n")
|
||||
unset(_temp_list)
|
||||
endif()
|
||||
|
||||
# Extra
|
||||
set(_extra_arguments)
|
||||
|
||||
if(FUNC_TARGETS)
|
||||
foreach(item IN LISTS FUNC_TARGETS)
|
||||
set(_extra_arguments
|
||||
"${_extra_arguments}INCLUDE_PATH += $<JOIN:$<TARGET_PROPERTY:${item},INCLUDE_DIRECTORIES>,${_sep}>\n\n")
|
||||
set(_extra_arguments
|
||||
"${_extra_arguments}PREDEFINED += $<JOIN:$<TARGET_PROPERTY:${item},COMPILE_DEFINITIONS>,${_sep}>\n\n")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(FUNC_OUTPUT_DIR)
|
||||
set(_doxy_output_dir ${FUNC_OUTPUT_DIR})
|
||||
endif()
|
||||
|
||||
if(_mdfile)
|
||||
set(_extra_arguments "${_extra_arguments}INPUT += ${_mdfile}\n\n")
|
||||
endif()
|
||||
|
||||
file(GENERATE
|
||||
OUTPUT "${_doxy_includes}"
|
||||
CONTENT "${_input}${_tagfiles}${_includes}${_defines}${_extra_arguments}${_no_expand}"
|
||||
)
|
||||
|
||||
set(_env)
|
||||
|
||||
foreach(_export IN LISTS FUNC_ENVIRONMENT_EXPORTS)
|
||||
if(NOT DEFINED "${_export}")
|
||||
message(FATAL_ERROR "qm_setup_doxygen: ${_export} is not known when trying to export it.")
|
||||
endif()
|
||||
|
||||
list(APPEND _env "${_export}=${${_export}}")
|
||||
endforeach()
|
||||
|
||||
list(APPEND _env "DOXY_FILE_DIR=${DOXYGEN_FILE_DIR}")
|
||||
list(APPEND _env "DOXY_INCLUDE_FILE=${_doxy_includes}")
|
||||
|
||||
list(APPEND _env "DOXY_PROJECT_NAME=${_name}")
|
||||
list(APPEND _env "DOXY_PROJECT_VERSION=${_version}")
|
||||
list(APPEND _env "DOXY_PROJECT_BRIEF=${_desc}")
|
||||
list(APPEND _env "DOXY_PROJECT_LOGO=${_logo}")
|
||||
list(APPEND _env "DOXY_MAINPAGE_MD_FILE=${_mdfile}")
|
||||
|
||||
set(_build_command "${CMAKE_COMMAND}" "-E" "env"
|
||||
${_env}
|
||||
"DOXY_OUTPUT_DIR=${_doxy_output_dir}"
|
||||
"DOXY_GENERATE_TAGFILE=${_tagfile}"
|
||||
"${DOXYGEN_EXECUTABLE}"
|
||||
"${DOXYGEN_FILE_DIR}/Doxyfile"
|
||||
)
|
||||
|
||||
if(FUNC_DEPENDS)
|
||||
set(_dependencies DEPENDS ${FUNC_DEPENDS})
|
||||
endif()
|
||||
|
||||
if(_tagfile)
|
||||
get_filename_component(_tagfile_dir ${_tagfile} ABSOLUTE)
|
||||
get_filename_component(_tagfile_dir ${_tagfile_dir} DIRECTORY)
|
||||
set(_make_tagfile_dir_cmd COMMAND ${CMAKE_COMMAND} -E make_directory ${_tagfile_dir})
|
||||
else()
|
||||
set(_make_tagfile_dir_cmd)
|
||||
endif()
|
||||
|
||||
add_custom_target(${_target}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${_doxy_output_dir}
|
||||
${_make_tagfile_dir_cmd}
|
||||
COMMAND ${_build_command}
|
||||
COMMENT "Building HTML documentation"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
VERBATIM
|
||||
${_dependencies}
|
||||
)
|
||||
|
||||
if(FUNC_INSTALL_DIR)
|
||||
set(_install_dir ${FUNC_INSTALL_DIR})
|
||||
|
||||
if(_tagfile)
|
||||
get_filename_component(_name ${_tagfile} NAME)
|
||||
set(_install_tagfile ${_name})
|
||||
else()
|
||||
set(_install_tagfile)
|
||||
endif()
|
||||
|
||||
set(_install_command "${CMAKE_COMMAND}" "-E" "env"
|
||||
${_env}
|
||||
"DOXY_OUTPUT_DIR=\${_install_dir}"
|
||||
"DOXY_GENERATE_TAGFILE=\${_install_dir}/${_install_tagfile}"
|
||||
"${DOXYGEN_EXECUTABLE}"
|
||||
"${DOXYGEN_FILE_DIR}/Doxyfile"
|
||||
)
|
||||
|
||||
set(_install_command_quoted)
|
||||
|
||||
foreach(_item IN LISTS _install_command)
|
||||
set(_install_command_quoted "${_install_command_quoted}\"${_item}\" ")
|
||||
endforeach()
|
||||
|
||||
install(CODE "
|
||||
message(STATUS \"Installing HTML documentation\")
|
||||
get_filename_component(_install_dir \"${_install_dir}\" ABSOLUTE BASE_DIR \${CMAKE_INSTALL_PREFIX})
|
||||
file(MAKE_DIRECTORY \${_install_dir})
|
||||
execute_process(
|
||||
COMMAND ${_install_command_quoted}
|
||||
WORKING_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}\"
|
||||
OUTPUT_QUIET
|
||||
)
|
||||
")
|
||||
endif()
|
||||
endfunction()
|
||||
199
libs/qwindowkit/qmsetup/cmake/modules/Filesystem.cmake
Normal file
199
libs/qwindowkit/qmsetup/cmake/modules/Filesystem.cmake
Normal file
@@ -0,0 +1,199 @@
|
||||
include_guard(DIRECTORY)
|
||||
|
||||
if(NOT QMSETUP_MODULES_DIR)
|
||||
get_filename_component(QMSETUP_MODULES_DIR ${CMAKE_CURRENT_LIST_DIR} DIRECTORY)
|
||||
endif()
|
||||
|
||||
#[[
|
||||
Initialize the build output directories of targets and resources.
|
||||
|
||||
qm_init_directories()
|
||||
]] #
|
||||
macro(qm_init_directories)
|
||||
if(NOT DEFINED QMSETUP_BUILD_DIR)
|
||||
set(QMSETUP_BUILD_DIR "${CMAKE_BINARY_DIR}/out-$<LOWER_CASE:${CMAKE_SYSTEM_PROCESSOR}>-$<CONFIG>")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${QMSETUP_BUILD_DIR}/bin)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${QMSETUP_BUILD_DIR}/lib)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${QMSETUP_BUILD_DIR}/lib)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_BUILD_SHARE_DIR)
|
||||
set(CMAKE_BUILD_SHARE_DIR ${QMSETUP_BUILD_DIR}/share)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
#[[
|
||||
Add a resources copying command for whole project.
|
||||
|
||||
qm_add_copy_command(<target>
|
||||
[CUSTOM_TARGET <target>]
|
||||
[EXTRA_ARGS <args...>]
|
||||
[DEPENDS <targets...>]
|
||||
[VERBOSE] [SKIP_BUILD] [SKIP_INSTALL]
|
||||
|
||||
SOURCES <file/dir...> [DESTINATION <dir>] [INSTALL_DIR <dir>]
|
||||
)
|
||||
|
||||
CUSTOM_TARGET: Use a custom target to control the copy command
|
||||
EXTRA_ARGS: Extra arguments to pass to file(INSTALL) statement
|
||||
DEPENDS: Targets that the copy command depends
|
||||
|
||||
SOURCES: Source files or directories, directories ending with "/" will have their contents copied
|
||||
DESTINATION: Copy the source file to the destination path. If the given value is a relative path,
|
||||
the base directory depends on the type of the target
|
||||
- `$<TARGET_FILE_DIR>`: real target
|
||||
- `QMSETUP_BUILD_DIR`: custom target
|
||||
INSTALL_DIR: Install the source files into a subdirectory in the given path. The subdirectory is the
|
||||
relative path from the `QMSETUP_BUILD_DIR` to `DESTINATION`.
|
||||
]] #
|
||||
function(qm_add_copy_command _target)
|
||||
set(options VERBOSE SKIP_BUILD SKIP_INSTALL)
|
||||
set(oneValueArgs CUSTOM_TARGET DESTINATION INSTALL_DIR)
|
||||
set(multiValueArgs SOURCES EXTRA_ARGS DEPENDS)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT FUNC_SOURCES)
|
||||
message(FATAL_ERROR "qm_add_copy_command: SOURCES not specified.")
|
||||
endif()
|
||||
|
||||
if(NOT TARGET ${_target})
|
||||
add_custom_target(${_target})
|
||||
endif()
|
||||
|
||||
# Determine destination
|
||||
set(_dest .)
|
||||
|
||||
if(FUNC_DESTINATION)
|
||||
set(_dest ${FUNC_DESTINATION})
|
||||
endif()
|
||||
|
||||
# Determine destination base directory
|
||||
get_target_property(_type ${_target} TYPE)
|
||||
|
||||
if(_type STREQUAL "UTILITY")
|
||||
if(QMSETUP_BUILD_DIR)
|
||||
set(_dest_base ${QMSETUP_BUILD_DIR})
|
||||
else()
|
||||
set(_dest_base ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endif()
|
||||
else()
|
||||
set(_dest_base "$<TARGET_FILE_DIR:${_target}>")
|
||||
endif()
|
||||
|
||||
# Set deploy target
|
||||
if(FUNC_CUSTOM_TARGET)
|
||||
set(_deploy_target ${FUNC_CUSTOM_TARGET})
|
||||
|
||||
if(NOT TARGET ${_deploy_target})
|
||||
add_custom_target(${_deploy_target})
|
||||
endif()
|
||||
else()
|
||||
set(_deploy_target ${_target})
|
||||
endif()
|
||||
|
||||
if(FUNC_DEPENDS)
|
||||
add_dependencies(${_deploy_target} ${FUNC_DEPENDS})
|
||||
endif()
|
||||
|
||||
# Prepare arguments
|
||||
set(_extra_args)
|
||||
set(_ignore_stdout)
|
||||
|
||||
if(FUNC_EXTRA_ARGS)
|
||||
list(APPEND _extra_args -D "args=${FUNC_EXTRA_ARGS}")
|
||||
endif()
|
||||
|
||||
if(NOT FUNC_VERBOSE)
|
||||
set(_ignore_stdout ${QMSETUP_IGNORE_STDOUT})
|
||||
endif()
|
||||
|
||||
if(NOT FUNC_SKIP_BUILD)
|
||||
# Build phase
|
||||
add_custom_command(TARGET ${_deploy_target} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-D "src=${FUNC_SOURCES}"
|
||||
-D "dest=${_dest}"
|
||||
-D "dest_base=${_dest_base}"
|
||||
${_extra_args}
|
||||
-P "${QMSETUP_MODULES_DIR}/scripts/copy.cmake" ${_ignore_stdout}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
if(FUNC_INSTALL_DIR AND NOT FUNC_SKIP_INSTALL)
|
||||
if(NOT QMSETUP_BUILD_DIR)
|
||||
message(FATAL_ERROR "qm_add_copy_command: `QMSETUP_BUILD_DIR` not defined, the install directory cannot be determined.")
|
||||
endif()
|
||||
|
||||
# Install phase
|
||||
install(CODE "
|
||||
set(_src \"${FUNC_SOURCES}\")
|
||||
set(_extra_args \"${FUNC_EXTRA_ARGS}\")
|
||||
|
||||
# Calculate the relative path from build phase destination to build directory
|
||||
get_filename_component(_build_dir \"${_dest}\" ABSOLUTE BASE_DIR \"${_dest_base}\")
|
||||
file(RELATIVE_PATH _rel_path \"${QMSETUP_BUILD_DIR}\" \${_build_dir})
|
||||
|
||||
# Calculate real install directory
|
||||
get_filename_component(_dest \"${FUNC_INSTALL_DIR}/\${_rel_path}\" ABSOLUTE BASE_DIR \${CMAKE_INSTALL_PREFIX})
|
||||
|
||||
execute_process(
|
||||
COMMAND \${CMAKE_COMMAND}
|
||||
-D \"src=\${_src}\"
|
||||
-D \"dest=\${_dest}\"
|
||||
\${_extra_args}
|
||||
-P \"${QMSETUP_MODULES_DIR}/scripts/copy.cmake\"
|
||||
WORKING_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}\"
|
||||
)
|
||||
")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
#[[
|
||||
Add a custom command to run `configure_file`.
|
||||
|
||||
qm_future_configure_file(<_input> <output>
|
||||
[FORCE]
|
||||
[EXTRA_ARGS <args...>]
|
||||
[DEPENDS <deps...>]
|
||||
[VARIABLES <var...>]
|
||||
)
|
||||
]] #
|
||||
function(qm_future_configure_file _input _output)
|
||||
set(options FORCE)
|
||||
set(oneValueArgs)
|
||||
set(multiValueArgs VARIABLES EXTRA_ARGS DEPENDS)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
set(_options)
|
||||
|
||||
foreach(_item IN LISTS FUNC_VARIABLES)
|
||||
list(APPEND _options -D "${_item}=${${_item}}")
|
||||
endforeach()
|
||||
|
||||
if(FUNC_FORCE)
|
||||
list(APPLE _options -D "force=TRUE")
|
||||
endif()
|
||||
|
||||
add_custom_command(OUTPUT ${_output}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-D "input=${_input}"
|
||||
-D "output=${_output}"
|
||||
-D "args=${FUNC_EXTRA_ARGS}"
|
||||
${_options}
|
||||
-P "${QMSETUP_MODULES_DIR}/scripts/configure_file.cmake"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${FUNC_DEPENDS}
|
||||
VERBATIM
|
||||
)
|
||||
endfunction()
|
||||
538
libs/qwindowkit/qmsetup/cmake/modules/Preprocess.cmake
Normal file
538
libs/qwindowkit/qmsetup/cmake/modules/Preprocess.cmake
Normal file
@@ -0,0 +1,538 @@
|
||||
#[[
|
||||
Warning: This module depends on `qmcorecmd` after installation.
|
||||
]] #
|
||||
if(NOT QMSETUP_CORECMD_EXECUTABLE)
|
||||
message(FATAL_ERROR "QMSETUP_CORECMD_EXECUTABLE not defined. Add find_package(qmsetup) to CMake first.")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED QMSETUP_DEFINITION_NUMERICAL)
|
||||
set(QMSETUP_DEFINITION_NUMERICAL off)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED QMSETUP_DEFINITION_SCOPE)
|
||||
set(QMSETUP_DEFINITION_SCOPE)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED QMSETUP_DEFINITION_PROPERTY)
|
||||
set(QMSETUP_DEFINITION_PROPERTY)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED QMSETUP_SYNC_INCLUDE_STANDARD)
|
||||
set(QMSETUP_SYNC_INCLUDE_STANDARD on)
|
||||
endif()
|
||||
|
||||
include_guard(DIRECTORY)
|
||||
|
||||
#[[
|
||||
Generate indirect reference files for header files to make the include statements more orderly.
|
||||
The generated file has the same timestamp as the source file.
|
||||
|
||||
qm_sync_include(<src> <dest>
|
||||
[STANDARD] [NO_STANDARD] [NO_ALL]
|
||||
[INCLUDE <expr> <sub> ...]
|
||||
[EXCLUDE <expr...>]
|
||||
[INSTALL_DIR <dir>]
|
||||
[FORCE] [VERBOSE]
|
||||
)
|
||||
|
||||
STANDARD: Enable standard public-private pattern, can be forced to enable by enabling `QMSETUP_SYNC_INCLUDE_STANDARD`
|
||||
NO_STANDARD: Disable standard public-private pattern, enable it to override `QMSETUP_SYNC_INCLUDE_STANDARD`
|
||||
#]]
|
||||
function(qm_sync_include _src_dir _dest_dir)
|
||||
set(options FORCE VERBOSE NO_STANDARD NO_ALL)
|
||||
set(oneValueArgs INSTALL_DIR)
|
||||
set(multiValueArgs INCLUDE EXCLUDE)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT IS_ABSOLUTE ${_src_dir})
|
||||
get_filename_component(_src_dir ${_src_dir} ABSOLUTE)
|
||||
else()
|
||||
string(REPLACE "\\" "/" _src_dir ${_src_dir})
|
||||
endif()
|
||||
|
||||
if(NOT IS_ABSOLUTE ${_dest_dir})
|
||||
get_filename_component(_dest_dir ${_dest_dir} ABSOLUTE)
|
||||
else()
|
||||
string(REPLACE "\\" "/" _dest_dir ${_dest_dir})
|
||||
endif()
|
||||
|
||||
if(IS_DIRECTORY ${_src_dir})
|
||||
file(GLOB_RECURSE header_files ${_src_dir}/*.h ${_src_dir}/*.hpp)
|
||||
|
||||
set(_args)
|
||||
|
||||
if(FUNC_STANDARD OR(QMSETUP_SYNC_INCLUDE_STANDARD AND NOT FUNC_NO_STANDARD))
|
||||
list(APPEND _args -s)
|
||||
endif()
|
||||
|
||||
if(FUNC_NO_ALL)
|
||||
list(APPEND _args -n)
|
||||
endif()
|
||||
|
||||
set(_even off)
|
||||
|
||||
foreach(_item IN LISTS FUNC_INCLUDE)
|
||||
if(_even)
|
||||
set(_even off)
|
||||
list(APPEND _args ${_item})
|
||||
else()
|
||||
set(_even on)
|
||||
list(APPEND _args -i ${_item})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(_item IN LISTS FUNC_EXCLUDE)
|
||||
list(APPEND _args -e ${_item})
|
||||
endforeach()
|
||||
|
||||
if(FUNC_VERBOSE)
|
||||
list(APPEND _args -V)
|
||||
endif()
|
||||
|
||||
if(FUNC_FORCE OR NOT EXISTS ${_dest_dir})
|
||||
if(EXISTS ${_dest_dir})
|
||||
file(REMOVE_RECURSE ${_dest_dir})
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${QMSETUP_CORECMD_EXECUTABLE} incsync ${_args} ${_src_dir} ${_dest_dir}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
endif()
|
||||
|
||||
if(FUNC_INSTALL_DIR)
|
||||
set(_install_dir ${FUNC_INSTALL_DIR})
|
||||
set(_args_quoted)
|
||||
|
||||
foreach(_item IN LISTS _args)
|
||||
set(_args_quoted "${_args_quoted}\"${_item}\" ")
|
||||
endforeach()
|
||||
|
||||
# Get command output only and use file(INSTALL) to install files
|
||||
install(CODE "
|
||||
get_filename_component(_install_dir \"${_install_dir}\" ABSOLUTE BASE_DIR \${CMAKE_INSTALL_PREFIX})
|
||||
|
||||
execute_process(
|
||||
COMMAND \"${QMSETUP_CORECMD_EXECUTABLE}\" incsync -d
|
||||
${_args_quoted} \"${_src_dir}\" \${_install_dir}
|
||||
WORKING_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}\"
|
||||
OUTPUT_VARIABLE _output_contents
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
string(REPLACE \"\\n\" \";\" _lines \"\${_output_contents}\")
|
||||
|
||||
foreach(_line IN LISTS _lines)
|
||||
string(REGEX MATCH \"from \\\"([^\\\"]*)\\\" to \\\"([^\\\"]*)\\\"\" _ \${_line})
|
||||
get_filename_component(_target_path \${CMAKE_MATCH_2} DIRECTORY)
|
||||
file(INSTALL \${CMAKE_MATCH_1} DESTINATION \${_target_path})
|
||||
endforeach()
|
||||
")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "qm_sync_include: source directory doesn't exist.")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
#[[
|
||||
Add a definition to a property scope.
|
||||
|
||||
qm_add_definition( <key | key=value> | <key> <value>
|
||||
[GLOBAL | TARGET <target> | SOURCE <file> | DIRECTORY <dir>]
|
||||
[PROPERTY <prop>]
|
||||
|
||||
[CONDITION <cond...>]
|
||||
[STRING_LITERAL] [NO_KEYWORD]
|
||||
[NUMERICAL] [CLASSICAL]
|
||||
)
|
||||
|
||||
STRING_LITERAL: Force quotes on values
|
||||
NO_KEYWORD: Treat any keyword as string
|
||||
NUMERICAL: Use 1/-1 as defined/undefined, can be forced to enable by enabling `QMSETUP_DEFINITION_NUMERICAL`
|
||||
CLASSICAL: Use classical definition, enable it to override `QMSETUP_DEFINITION_NUMERICAL`
|
||||
]] #
|
||||
function(qm_add_definition _first)
|
||||
set(options STRING_LITERAL NO_KEYWORD NUMERICAL CLASSICAL)
|
||||
set(oneValueArgs TARGET SOURCE DIRECTORY PROPERTY)
|
||||
set(multiValueArgs CONDITION)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
set(_result)
|
||||
set(_is_pair off)
|
||||
set(_defined off)
|
||||
|
||||
set(_list ${_first} ${FUNC_UNPARSED_ARGUMENTS})
|
||||
list(LENGTH _list _len)
|
||||
|
||||
set(_cond on)
|
||||
set(_numerical off)
|
||||
|
||||
if(NOT FUNC_CLASSICAL AND(QMSETUP_DEFINITION_NUMERICAL OR FUNC_NUMERICAL))
|
||||
set(_numerical on)
|
||||
endif()
|
||||
|
||||
if(FUNC_CONDITION)
|
||||
if(NOT(${FUNC_CONDITION}))
|
||||
set(_cond off)
|
||||
endif()
|
||||
elseif(DEFINED FUNC_CONDITION)
|
||||
set(_cond off)
|
||||
endif()
|
||||
|
||||
if(_len EQUAL 1)
|
||||
set(_result ${_list})
|
||||
set(_defined on)
|
||||
|
||||
if(NOT _cond)
|
||||
set(_defined off)
|
||||
endif()
|
||||
elseif(_len EQUAL 2)
|
||||
# Get key
|
||||
list(POP_FRONT _list _key)
|
||||
list(POP_FRONT _list _val)
|
||||
|
||||
if(FUNC_STRING_LITERAL AND NOT ${_val} MATCHES "\".+\"")
|
||||
set(_val "\"${_val}\"")
|
||||
endif()
|
||||
|
||||
# Boolean
|
||||
string(TOLOWER ${_val} _val_lower)
|
||||
|
||||
if(NOT FUNC_NO_KEYWORD AND(${_val_lower} STREQUAL "off" OR ${_val_lower} STREQUAL "false"))
|
||||
set(_result ${_key})
|
||||
set(_defined off)
|
||||
|
||||
if(NOT _cond)
|
||||
set(_defined on)
|
||||
endif()
|
||||
elseif(NOT FUNC_NO_KEYWORD AND(${_val_lower} STREQUAL "on" OR ${_val_lower} STREQUAL "true"))
|
||||
set(_result ${_key})
|
||||
set(_defined on)
|
||||
|
||||
if(NOT _cond)
|
||||
set(_defined off)
|
||||
endif()
|
||||
else()
|
||||
set(_result "${_key}=${_val}")
|
||||
set(_is_pair on)
|
||||
set(_defined on)
|
||||
|
||||
if(NOT _cond)
|
||||
set(_defined off)
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "qm_add_definition: called with incorrect number of arguments")
|
||||
endif()
|
||||
|
||||
if(_numerical AND NOT _is_pair)
|
||||
if(_defined)
|
||||
set(_result "${_result}=1")
|
||||
else()
|
||||
set(_result "${_result}=-1")
|
||||
endif()
|
||||
elseif(NOT _defined)
|
||||
return()
|
||||
endif()
|
||||
|
||||
_qm_calc_property_scope_helper(_scope _prop)
|
||||
set_property(${_scope} APPEND PROPERTY ${_prop} "${_result}")
|
||||
endfunction()
|
||||
|
||||
#[[
|
||||
Remove a definition from a property scope.
|
||||
|
||||
qm_remove_definition(<key>
|
||||
[GLOBAL | TARGET <target> | SOURCE <file> | DIRECTORY <dir>]
|
||||
[PROPERTY <prop>]
|
||||
)
|
||||
]] #
|
||||
function(qm_remove_definition _key)
|
||||
set(options)
|
||||
set(oneValueArgs TARGET PROPERTY)
|
||||
set(multiValueArgs)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(FUNC_TARGET)
|
||||
get_target_property(_definitions ${FUNC_TARGET} ${_prop})
|
||||
else()
|
||||
get_property(_definitions GLOBAL PROPERTY ${_prop})
|
||||
endif()
|
||||
|
||||
# Filter
|
||||
list(FILTER _definitions EXCLUDE REGEX "^${_key}(=.*)?$")
|
||||
|
||||
_qm_calc_property_scope_helper(_scope _prop)
|
||||
set_property(${_scope} PROPERTY ${_prop} "${_definitions}")
|
||||
endfunction()
|
||||
|
||||
#[[
|
||||
Generate a configuration header of a property scope. If the configuration has not changed,
|
||||
the generated file's timestemp will not be updated when you reconfigure it.
|
||||
|
||||
qm_generate_config(<file>
|
||||
[GLOBAL | TARGET <target> | SOURCE <file> | DIRECTORY <dir>]
|
||||
[PROPERTY <prop>]
|
||||
|
||||
[PROJECT_NAME <name>]
|
||||
[WARNING_FILE <file>]
|
||||
[NO_WARNING]
|
||||
[NO_HASH]
|
||||
)
|
||||
]] #
|
||||
function(qm_generate_config _file)
|
||||
set(options NO_WARNING NO_HASH)
|
||||
set(oneValueArgs TARGET PROPERTY PROJECT_NAME WARNING_FILE)
|
||||
set(multiValueArgs)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
_qm_calc_property_scope_helper(_scope _prop)
|
||||
get_property(_definitions ${_scope} PROPERTY ${_prop})
|
||||
|
||||
if(NOT _definitions)
|
||||
set(_definitions) # May be _-NOTFOUND
|
||||
endif()
|
||||
|
||||
_qm_generate_config_helper()
|
||||
endfunction()
|
||||
|
||||
#[[
|
||||
Generate build info information header.
|
||||
|
||||
qm_generate_build_info(<file>
|
||||
[REQUIRED]
|
||||
[ROOT_DIRECTORY <dir>]
|
||||
[PREFIX <prefix>]
|
||||
[YEAR] [TIME]
|
||||
|
||||
[PROJECT_NAME <name>]
|
||||
[WARNING_FILE <file>]
|
||||
[NO_WARNING]
|
||||
[NO_HASH]
|
||||
)
|
||||
|
||||
file: Output file
|
||||
|
||||
REQUIRED: Abort if there's any error with git
|
||||
ROOT_DIRECTORY: Repository root directory (CMake will try to run `git` at this directory)
|
||||
PREFIX: Macros prefix, default to the upper case of `PROJECT_NAME`
|
||||
]] #
|
||||
function(qm_generate_build_info _file)
|
||||
set(options NO_WARNING NO_HASH YEAR TIME REQUIRED)
|
||||
set(oneValueArgs ROOT_DIRECTORY PREFIX PROJECT_NAME WARNING_FILE)
|
||||
set(multiValueArgs)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(FUNC_PREFIX)
|
||||
set(_prefix ${FUNC_PREFIX})
|
||||
else()
|
||||
string(TOUPPER "${PROJECT_NAME}" _prefix)
|
||||
string(MAKE_C_IDENTIFIER ${_prefix} _prefix)
|
||||
endif()
|
||||
|
||||
set(_dir)
|
||||
qm_set_value(_dir FUNC_ROOT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
set(_git_branch "unknown")
|
||||
set(_git_hash "unknown")
|
||||
set(_git_commit_time "unknown")
|
||||
set(_git_commit_author "unknown")
|
||||
set(_git_commit_email "unknown")
|
||||
set(_git_revision_id "0")
|
||||
|
||||
# Check `git` command
|
||||
if(NOT GIT_EXECUTABLE)
|
||||
find_package(Git)
|
||||
|
||||
if(NOT Git_FOUND)
|
||||
if(FUNC_REQUIRED)
|
||||
message(FATAL_ERROR "Git not found")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(GIT_EXECUTABLE)
|
||||
# Branch
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} symbolic-ref --short -q HEAD
|
||||
OUTPUT_VARIABLE _temp
|
||||
ERROR_VARIABLE _err
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
WORKING_DIRECTORY ${_dir}
|
||||
RESULT_VARIABLE _code
|
||||
)
|
||||
|
||||
if(_code EQUAL 0)
|
||||
set(_git_branch ${_temp})
|
||||
elseif(FUNC_REQUIRED)
|
||||
message(FATAL_ERROR "Error running git symbolic-ref: ${_err}")
|
||||
endif()
|
||||
|
||||
# Hash
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} log -1 "--pretty=format:%H;%aI;%aN;%aE" # Use `;` as separator
|
||||
OUTPUT_VARIABLE _temp
|
||||
ERROR_VARIABLE _err
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
WORKING_DIRECTORY ${_dir}
|
||||
RESULT_VARIABLE _code
|
||||
)
|
||||
|
||||
if(_code EQUAL 0)
|
||||
list(GET _temp 0 _git_hash)
|
||||
list(GET _temp 1 _git_commit_time)
|
||||
list(GET _temp 2 _git_commit_author)
|
||||
list(GET _temp 3 _git_commit_email)
|
||||
elseif(FUNC_REQUIRED)
|
||||
message(FATAL_ERROR "Error running git log: ${_err}")
|
||||
endif()
|
||||
|
||||
# Revision ID
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-list --count HEAD
|
||||
OUTPUT_VARIABLE _temp
|
||||
ERROR_VARIABLE _err
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
WORKING_DIRECTORY ${_dir}
|
||||
RESULT_VARIABLE _code
|
||||
)
|
||||
|
||||
if(_code EQUAL 0)
|
||||
set(_git_revision_id ${_temp})
|
||||
elseif(FUNC_REQUIRED)
|
||||
message(FATAL_ERROR "Error running git rev-list: ${_err}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
qm_set_value(_system_name CMAKE_SYSTEM_NAME unknown)
|
||||
qm_set_value(_system_version CMAKE_SYSTEM_VERSION unknown)
|
||||
qm_set_value(_system_processor CMAKE_SYSTEM_PROCESSOR unknown)
|
||||
|
||||
qm_set_value(_host_system_name CMAKE_HOST_SYSTEM_NAME unknown)
|
||||
qm_set_value(_host_system_version CMAKE_HOST_SYSTEM_VERSION unknown)
|
||||
qm_set_value(_host_system_processor CMAKE_HOST_SYSTEM_PROCESSOR unknown)
|
||||
|
||||
qm_set_value(_compiler_name CMAKE_CXX_COMPILER_ID unknown)
|
||||
qm_set_value(_compiler_version CMAKE_CXX_COMPILER_VERSION unknown)
|
||||
qm_set_value(_compiler_arch CMAKE_CXX_COMPILER_ARCHITECTURE_ID unknown)
|
||||
qm_set_value(_compiler_abi CMAKE_CXX_COMPILER_ABI unknown)
|
||||
|
||||
set(_definitions)
|
||||
|
||||
set(_has_time off)
|
||||
|
||||
# year
|
||||
if(FUNC_YEAR)
|
||||
string(TIMESTAMP _build_year "%Y")
|
||||
list(APPEND _definitions ${_prefix}_BUILD_YEAR=\"${_build_year}\")
|
||||
set(_has_time on)
|
||||
endif()
|
||||
|
||||
# time
|
||||
if(FUNC_TIME)
|
||||
string(TIMESTAMP _build_time UTC)
|
||||
list(APPEND _definitions ${_prefix}_BUILD_TIME=\"${_build_time}\")
|
||||
set(_has_time on)
|
||||
endif()
|
||||
|
||||
if(_has_time)
|
||||
list(APPEND _definitions "%")
|
||||
endif()
|
||||
|
||||
# system
|
||||
list(APPEND _definitions ${_prefix}_SYSTEM_NAME=\"${_system_name}\")
|
||||
list(APPEND _definitions ${_prefix}_SYSTEM_VERSION=\"${_system_version}\")
|
||||
list(APPEND _definitions ${_prefix}_SYSTEM_PROCESSOR=\"${_system_processor}\")
|
||||
|
||||
list(APPEND _definitions ${_prefix}_HOST_SYSTEM_NAME=\"${_host_system_name}\")
|
||||
list(APPEND _definitions ${_prefix}_HOST_SYSTEM_VERSION=\"${_host_system_version}\")
|
||||
list(APPEND _definitions ${_prefix}_HOST_SYSTEM_PROCESSOR=\"${_host_system_processor}\")
|
||||
|
||||
list(APPEND _definitions "%")
|
||||
|
||||
# compiler
|
||||
list(APPEND _definitions ${_prefix}_COMPILER_ID=\"${_compiler_name}\")
|
||||
list(APPEND _definitions ${_prefix}_COMPILER_VERSION=\"${_compiler_version}\")
|
||||
list(APPEND _definitions ${_prefix}_COMPILER_ARCH=\"${_compiler_arch}\")
|
||||
list(APPEND _definitions ${_prefix}_COMPILER_ABI=\"${_compiler_abi}\")
|
||||
|
||||
list(APPEND _definitions "%")
|
||||
|
||||
# build time (deprecated)
|
||||
# list(APPEND _definitions ${_prefix}_BUILD_DATE_TIME=\"${_build_time}\")
|
||||
# list(APPEND _definitions ${_prefix}_BUILD_YEAR=\"${_build_year}\")
|
||||
|
||||
# list(APPEND _definitions "%")
|
||||
|
||||
# git info
|
||||
list(APPEND _definitions ${_prefix}_GIT_BRANCH=\"${_git_branch}\")
|
||||
list(APPEND _definitions ${_prefix}_GIT_LAST_COMMIT_HASH=\"${_git_hash}\")
|
||||
list(APPEND _definitions ${_prefix}_GIT_LAST_COMMIT_TIME=\"${_git_commit_time}\")
|
||||
list(APPEND _definitions ${_prefix}_GIT_LAST_COMMIT_AUTHOR=\"${_git_commit_author}\")
|
||||
list(APPEND _definitions ${_prefix}_GIT_LAST_COMMIT_EMAIL=\"${_git_commit_email}\")
|
||||
list(APPEND _definitions ${_prefix}_GIT_REVISION_ID=\"${_git_revision_id}\")
|
||||
|
||||
_qm_generate_config_helper()
|
||||
endfunction()
|
||||
|
||||
# ----------------------------------
|
||||
# Private functions
|
||||
# ----------------------------------
|
||||
function(_qm_calc_property_scope_helper _scope _prop)
|
||||
if(FUNC_TARGET)
|
||||
set(_scope TARGET ${FUNC_TARGET})
|
||||
elseif(FUNC_SOURCE)
|
||||
set(_scope SOURCE ${FUNC__SOURCE})
|
||||
elseif(FUNC_DIRECTORY)
|
||||
set(_scope DIRECTORY ${FUNC_DIRECTORY})
|
||||
elseif(FUNC_GLOBAL)
|
||||
set(_scope GLOBAL)
|
||||
elseif(QMSETUP_DEFINITION_SCOPE)
|
||||
set(_scope ${QMSETUP_DEFINITION_SCOPE})
|
||||
else()
|
||||
set(_scope GLOBAL)
|
||||
endif()
|
||||
|
||||
qm_set_value(_prop FUNC_PROPERTY QMSETUP_DEFINITION_PROPERTY "CONFIG_DEFINITIONS")
|
||||
|
||||
set(_scope ${_scope} PARENT_SCOPE)
|
||||
set(_prop ${_prop} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_qm_generate_config_helper)
|
||||
set(_args)
|
||||
|
||||
foreach(_item IN LISTS _definitions)
|
||||
list(APPEND _args "-D${_item}")
|
||||
endforeach()
|
||||
|
||||
if(FUNC_PROJECT_NAME)
|
||||
list(APPEND _args "-p" ${FUNC_PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
if(FUNC_NO_HASH)
|
||||
list(APPEND _args "-f")
|
||||
endif()
|
||||
|
||||
list(APPEND _args ${_file})
|
||||
|
||||
if(NOT FUNC_NO_WARNING)
|
||||
list(APPEND _args "-w")
|
||||
|
||||
if(FUNC_WARNING_FILE)
|
||||
list(APPEND _args ${FUNC_WARNING_FILE})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND ${QMSETUP_CORECMD_EXECUTABLE} configure ${_args}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
endfunction()
|
||||
141
libs/qwindowkit/qmsetup/cmake/modules/Protobuf.cmake
Normal file
141
libs/qwindowkit/qmsetup/cmake/modules/Protobuf.cmake
Normal file
@@ -0,0 +1,141 @@
|
||||
include_guard(DIRECTORY)
|
||||
|
||||
#[[
|
||||
Add rules for creating Google Protobuf source files.
|
||||
|
||||
qm_create_protobuf(<OUT>
|
||||
INPUT <files...>
|
||||
[OUTPUT_DIR <dir>]
|
||||
[TARGET <target>]
|
||||
[INCLUDE_DIRECTORIES <dirs...>]
|
||||
[OPTIONS <options...>]
|
||||
[DEPENDS <deps...>]
|
||||
[CREATE_ONCE]
|
||||
)
|
||||
|
||||
INPUT: source files
|
||||
OUTPUT_DIR: output directory
|
||||
|
||||
TARGET: add a custom target to run the generating command
|
||||
|
||||
INCLUDE_DIRECTORIES: extra include directories
|
||||
OPTIONS: extra options passed to protobuf compiler
|
||||
DEPENDS: dependencies
|
||||
|
||||
CREATE_ONCE: create proto code files at configure phase if not exist
|
||||
|
||||
OUT: output source file paths
|
||||
#]]
|
||||
function(qm_create_protobuf _out)
|
||||
set(options CREATE_ONCE)
|
||||
set(oneValueArgs OUTPUT_DIR TARGET)
|
||||
set(multiValueArgs INPUT INCLUDE_DIRECTORIES OPTIONS DEPENDS)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# Find `protoc`
|
||||
if(NOT PROTOC_EXECUTABLE)
|
||||
if(NOT TARGET protobuf::protoc)
|
||||
message(FATAL_ERROR "qm_create_protobuf: protobuf compiler not found. Add find_package(Protobuf) to CMake to enable.")
|
||||
endif()
|
||||
|
||||
get_target_property(PROTOC_EXECUTABLE protobuf::protoc LOCATION)
|
||||
|
||||
if(NOT PROTOC_EXECUTABLE)
|
||||
message(FATAL_ERROR "qm_create_protobuf: failed to get the location of `protoc`, you could set PROTOC_EXECUTABLE manually.")
|
||||
endif()
|
||||
|
||||
# Cache value
|
||||
set(PROTOC_EXECUTABLE ${PROTOC_EXECUTABLE} PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
if(NOT FUNC_INPUT)
|
||||
message(FATAL_ERROR "qm_create_protobuf: INPUT not specified.")
|
||||
endif()
|
||||
|
||||
if(FUNC_OUTPUT_DIR)
|
||||
get_filename_component(_out_dir ${FUNC_OUTPUT_DIR} ABSOLUTE)
|
||||
file(MAKE_DIRECTORY ${_out_dir})
|
||||
else()
|
||||
set(_out_dir ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
# Collect include paths and out files
|
||||
set(_include_options)
|
||||
set(_out_files)
|
||||
set(_input_names)
|
||||
|
||||
foreach(_item IN LISTS FUNC_INCLUDE_DIRECTORIES)
|
||||
get_filename_component(_abs_path ${_item} ABSOLUTE)
|
||||
list(APPEND _include_options -I${_abs_path})
|
||||
endforeach()
|
||||
|
||||
foreach(_item IN LISTS FUNC_INPUT)
|
||||
get_filename_component(_item ${_item} ABSOLUTE)
|
||||
get_filename_component(_abs_path ${_item} DIRECTORY)
|
||||
list(APPEND _include_options -I${_abs_path})
|
||||
endforeach()
|
||||
|
||||
list(REMOVE_DUPLICATES _include_options)
|
||||
|
||||
set(_create_once_warning)
|
||||
set(_create_once_warning_printed off)
|
||||
|
||||
# Prepare for create once
|
||||
if(FUNC_CREATE_ONCE)
|
||||
# Check if options contain generator expressions
|
||||
foreach(_opt IN LISTS _include_options LISTS FUNC_OPTIONS)
|
||||
string(GENEX_STRIP "${_opt}" _no_genex)
|
||||
|
||||
if(NOT _no_genex STREQUAL _opt)
|
||||
set(_create_once_warning "options contain generator expressions, skip generating source file now")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
foreach(_item IN LISTS FUNC_INPUT)
|
||||
get_filename_component(_basename ${_item} NAME_WLE)
|
||||
list(APPEND _out_files ${_out_dir}/${_basename}.pb.h ${_out_dir}/${_basename}.pb.cc)
|
||||
|
||||
get_filename_component(_name ${_item} NAME)
|
||||
list(APPEND _input_names ${_name})
|
||||
|
||||
if(FUNC_CREATE_ONCE AND(NOT EXISTS ${_out_dir}/${_basename}.pb.h OR NOT EXISTS ${_out_dir}/${_basename}.pb.cc))
|
||||
if(_create_once_warning)
|
||||
if(NOT _create_once_warning_printed)
|
||||
message(WARNING "qm_create_protobuf: ${_create_once_warning}")
|
||||
set(_create_once_warning_printed on)
|
||||
endif()
|
||||
else()
|
||||
get_filename_component(_abs_file ${_item} ABSOLUTE)
|
||||
|
||||
if(NOT EXISTS ${_abs_file})
|
||||
message(WARNING "qm_create_protobuf: input file \"${_name}\" is not available, skip generating source file now")
|
||||
else()
|
||||
message(STATUS "Protoc: Generating ${_basename}.pb.h, ${_basename}.pb.cc")
|
||||
execute_process(COMMAND
|
||||
${PROTOC_EXECUTABLE} --cpp_out=${_out_dir} ${_include_options} ${FUNC_OPTIONS} ${_name}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${_out_dir}/${_basename}.pb.h ${_out_dir}/${_basename}.pb.cc
|
||||
COMMAND ${PROTOC_EXECUTABLE} --cpp_out=${_out_dir} ${_include_options} ${FUNC_OPTIONS} ${_name}
|
||||
DEPENDS ${_item} ${FUNC_DEPENDS}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
VERBATIM
|
||||
)
|
||||
endforeach()
|
||||
|
||||
if(FUNC_TARGET)
|
||||
if(NOT TARGET ${FUNC_TARGET})
|
||||
add_custom_target(${FUNC_TARGET})
|
||||
endif()
|
||||
|
||||
add_dependencies(${FUNC_TARGET} ${_out_files})
|
||||
endif()
|
||||
|
||||
set(${_out} ${_out_files} PARENT_SCOPE)
|
||||
endfunction()
|
||||
96
libs/qwindowkit/qmsetup/cmake/modules/Qml.cmake
Normal file
96
libs/qwindowkit/qmsetup/cmake/modules/Qml.cmake
Normal file
@@ -0,0 +1,96 @@
|
||||
include_guard(DIRECTORY)
|
||||
|
||||
#[[
|
||||
Installs a QML module and its runtime loadable plugin, meta information, QML files, and resources.
|
||||
The module is identified by the given target name.
|
||||
|
||||
The Qt version should be greater than or equal to 6.3.
|
||||
|
||||
qm_install_qml_modules(target
|
||||
[PREFIX prefix]
|
||||
)
|
||||
|
||||
Arguments:
|
||||
PREFIX: install directory prefix (default: "qml")
|
||||
|
||||
Notice:
|
||||
For static library backing targets, you should specify "OUTPUT_TARGETS" when calling "qt_add_qml_module()"
|
||||
to collect the internally generated targets (mainly object libraries), and then install them by calling:
|
||||
|
||||
install(TARGETS ${_output_targets}
|
||||
EXPORT <target set>
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
OBJECTS DESTINATION lib
|
||||
)
|
||||
|
||||
See also: https://doc.qt.io/qt-6/qt-add-qml-module.html
|
||||
|
||||
]] #
|
||||
function(qm_install_qml_modules _target)
|
||||
set(options)
|
||||
set(oneValueArgs PREFIX)
|
||||
set(multiValueArgs)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
set(_prefix)
|
||||
qm_set_value(_prefix FUNC_PREFIX "qml")
|
||||
|
||||
qt_query_qml_module(${_target}
|
||||
URI _module_uri
|
||||
VERSION _module_version
|
||||
PLUGIN_TARGET _module_plugin_target
|
||||
TARGET_PATH _module_target_path
|
||||
QMLDIR _module_qmldir
|
||||
TYPEINFO _module_typeinfo
|
||||
QML_FILES _module_qml_files
|
||||
QML_FILES_DEPLOY_PATHS _qml_files_deploy_paths
|
||||
RESOURCES _module_resources
|
||||
RESOURCES_DEPLOY_PATHS _resources_deploy_paths
|
||||
)
|
||||
|
||||
# See also: https://doc.qt.io/qt-6/qt-query-qml-module.html#example
|
||||
|
||||
# Install the QML module runtime loadable plugin
|
||||
set(_module_dir "${_prefix}/${_module_target_path}")
|
||||
install(TARGETS "${_module_plugin_target}"
|
||||
LIBRARY DESTINATION "${_module_dir}"
|
||||
RUNTIME DESTINATION "${_module_dir}"
|
||||
ARCHIVE DESTINATION "${_module_dir}"
|
||||
)
|
||||
|
||||
# Install the QML module meta information.
|
||||
install(FILES "${_module_qmldir}" DESTINATION "${_module_dir}")
|
||||
install(FILES "${_module_typeinfo}" DESTINATION "${_module_dir}")
|
||||
|
||||
# Install QML files, possibly renamed.
|
||||
list(LENGTH _module_qml_files _num_files)
|
||||
|
||||
if(_num_files GREATER 0)
|
||||
math(EXPR _last_index "${_num_files} - 1")
|
||||
|
||||
foreach(_i RANGE 0 ${_last_index})
|
||||
list(GET _module_qml_files ${_i} _src_file)
|
||||
list(GET _qml_files_deploy_paths ${_i} _deploy_path)
|
||||
get_filename_component(_dest_name "${_deploy_path}" NAME)
|
||||
get_filename_component(_dest_dir "${_deploy_path}" DIRECTORY)
|
||||
install(FILES "${_src_file}" DESTINATION "${_module_dir}/${_dest_dir}" RENAME "${_dest_name}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Install resources, possibly renamed.
|
||||
list(LENGTH _module_resources _num_files)
|
||||
|
||||
if(_num_files GREATER 0)
|
||||
math(EXPR _last_index "${_num_files} - 1")
|
||||
|
||||
foreach(_i RANGE 0 ${_last_index})
|
||||
list(GET _module_resources ${_i} _src_file)
|
||||
list(GET _resources_deploy_paths ${_i} _deploy_path)
|
||||
get_filename_component(_dest_name "${_deploy_path}" NAME)
|
||||
get_filename_component(_dest_dir "${_deploy_path}" DIRECTORY)
|
||||
install(FILES "${_src_file}" DESTINATION "${_module_dir}/${_dest_dir}" RENAME "${_dest_name}")
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
374
libs/qwindowkit/qmsetup/cmake/modules/Translate.cmake
Normal file
374
libs/qwindowkit/qmsetup/cmake/modules/Translate.cmake
Normal file
@@ -0,0 +1,374 @@
|
||||
include_guard(DIRECTORY)
|
||||
|
||||
#[[
|
||||
Add qt translation target.
|
||||
|
||||
qm_add_translation(<target>
|
||||
[LOCALES locales]
|
||||
[PREFIX prefix]
|
||||
[SOURCES files... | DIRECTORIES dirs... | TARGETS targets... | TS_FILES files...]
|
||||
[TS_DIR dir]
|
||||
[QM_DIR dir]
|
||||
[TS_OPTIONS options...]
|
||||
[QM_OPTIONS options...]
|
||||
[TS_DEPENDS targets...]
|
||||
[QM_DEPENDS targets...]
|
||||
[CREATE_ONCE]
|
||||
)
|
||||
|
||||
Arguments:
|
||||
LOCALES: language names, e.g. zh_CN en_US, must specify if SOURCES or TARGETS is specified
|
||||
PREFIX: translation file prefix, default to target name
|
||||
|
||||
SOURCES: source files
|
||||
DIRECTORIES: source directories
|
||||
TARGETS: target names, the source files of which will be collected
|
||||
TS_FILES: ts file names, add the specified ts file
|
||||
|
||||
TS_DIR: ts files destination, default to `CMAKE_CURRENT_SOURCE_DIR`
|
||||
QM_DIR: qm files destination, default to `CMAKE_CURRENT_BINARY_DIR`
|
||||
|
||||
TS_DEPENDS: add lupdate task as a dependency to the given targets
|
||||
QM_DEPENDS: add lrelease task as a dependency to the given targets
|
||||
|
||||
CREATE_ONCE: create translations at configure phase if not exist
|
||||
|
||||
]] #
|
||||
function(qm_add_translation _target)
|
||||
set(options CREATE_ONCE)
|
||||
set(oneValueArgs PREFIX TS_DIR QM_DIR)
|
||||
set(multiValueArgs LOCALES SOURCES DIRECTORIES TARGETS TS_FILES TS_OPTIONS QM_OPTIONS TS_DEPENDS QM_DEPENDS)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# Get linguist tools
|
||||
if(NOT TARGET Qt${QT_VERSION_MAJOR}::lupdate OR NOT TARGET Qt${QT_VERSION_MAJOR}::lrelease)
|
||||
message(FATAL_ERROR "qm_add_translation: linguist tools not defined. Add find_package(Qt\${QT_VERSION_MAJOR} COMPONENTS LinguistTools) to CMake to enable.")
|
||||
endif()
|
||||
|
||||
set(_src_files)
|
||||
set(_include_dirs)
|
||||
|
||||
# Collect source files
|
||||
if(FUNC_SOURCES)
|
||||
list(APPEND _src_files ${FUNC_SOURCES})
|
||||
endif()
|
||||
|
||||
# Collect source files
|
||||
if(FUNC_TARGETS)
|
||||
foreach(_item IN LISTS FUNC_TARGETS)
|
||||
get_target_property(_type ${_item} TYPE)
|
||||
|
||||
if((_type STREQUAL "UTILITY") OR(_type STREQUAL "INTERFACE_LIBRARY"))
|
||||
continue()
|
||||
endif()
|
||||
|
||||
set(_tmp_files)
|
||||
get_target_property(_tmp_files ${_item} SOURCES)
|
||||
set(_qml_files)
|
||||
get_target_property(_qml_files ${_item} QT_QML_MODULE_QML_FILES)
|
||||
if (NOT _qml_files STREQUAL ${_item}-NOTFOUND)
|
||||
list(APPEND _tmp_files ${_qml_files})
|
||||
endif()
|
||||
list(FILTER _tmp_files INCLUDE REGEX ".+\\.(h|hh|hpp|hxx|c|cc|cpp|cxx|m|mm|qml|js|mjs)$")
|
||||
list(FILTER _tmp_files EXCLUDE REGEX "^(qasc|moc)_.+")
|
||||
|
||||
# Need to convert to absolute path
|
||||
get_target_property(_target_dir ${_item} SOURCE_DIR)
|
||||
|
||||
foreach(_file IN LISTS _tmp_files)
|
||||
get_filename_component(_abs_file ${_file} ABSOLUTE BASE_DIR ${_target_dir})
|
||||
list(APPEND _src_files ${_abs_file})
|
||||
endforeach()
|
||||
|
||||
unset(_tmp_files)
|
||||
|
||||
get_target_property(_tmp_dirs ${_item} INCLUDE_DIRECTORIES)
|
||||
list(APPEND _include_dirs ${_tmp_dirs})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Collect source directories
|
||||
if(FUNC_DIRECTORIES)
|
||||
foreach(_item IN LISTS FUNC_DIRECTORIES)
|
||||
file(GLOB _tmp
|
||||
${_item}/*.h ${_item}/*.hpp
|
||||
${_item}/*.hh ${_item}/*.hxx
|
||||
${_item}/*.cpp ${_item}/*.cxx
|
||||
${_item}/*.c ${_item}/*.cc
|
||||
${_item}/*.m ${_item}/*.mm
|
||||
)
|
||||
list(APPEND _src_files ${_tmp})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(_src_files)
|
||||
if(NOT FUNC_LOCALES)
|
||||
message(FATAL_ERROR "qm_add_translation: source files collected but LOCALES not specified!")
|
||||
endif()
|
||||
elseif(NOT FUNC_TS_FILES)
|
||||
message(FATAL_ERROR "qm_add_translation: no source files or ts files collected!")
|
||||
endif()
|
||||
|
||||
add_custom_target(${_target})
|
||||
|
||||
set(_qm_depends)
|
||||
|
||||
if(FUNC_TS_OPTIONS)
|
||||
set(_ts_options ${FUNC_TS_OPTIONS})
|
||||
endif()
|
||||
|
||||
if(FUNC_QM_OPTIONS)
|
||||
set(_qm_options ${FUNC_QM_OPTIONS})
|
||||
endif()
|
||||
|
||||
if(_src_files)
|
||||
if(FUNC_PREFIX)
|
||||
set(_prefix ${FUNC_PREFIX})
|
||||
else()
|
||||
set(_prefix ${_target})
|
||||
endif()
|
||||
|
||||
if(FUNC_TS_DIR)
|
||||
set(_ts_dir ${FUNC_TS_DIR})
|
||||
else()
|
||||
set(_ts_dir ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
set(_ts_files)
|
||||
|
||||
foreach(_loc IN LISTS FUNC_LOCALES)
|
||||
list(APPEND _ts_files ${_ts_dir}/${_prefix}_${_loc}.ts)
|
||||
endforeach()
|
||||
|
||||
# Include options
|
||||
set(_include_options)
|
||||
|
||||
foreach(_inc IN LISTS _include_dirs)
|
||||
list(APPEND _include_options "-I${_inc}")
|
||||
endforeach()
|
||||
|
||||
# May be an lupdate bug, so we skip passing include directories
|
||||
# list(APPEND _ts_options ${_include_options})
|
||||
if(_ts_options)
|
||||
list(PREPEND _ts_options OPTIONS)
|
||||
endif()
|
||||
|
||||
set(_create_once)
|
||||
|
||||
if(FUNC_CREATE_ONCE)
|
||||
set(_create_once CREATE_ONCE)
|
||||
endif()
|
||||
|
||||
_qm_add_lupdate_target(${_target}_lupdate
|
||||
INPUT ${_src_files}
|
||||
OUTPUT ${_ts_files}
|
||||
${_ts_options}
|
||||
${_create_once}
|
||||
)
|
||||
|
||||
# Add update dependencies
|
||||
# add_dependencies(${_target} ${_target}_lupdate)
|
||||
foreach(_item IN LISTS FUNC_TS_DEPENDS)
|
||||
add_dependencies(${_item} ${_target}_lupdate)
|
||||
endforeach()
|
||||
|
||||
# list(APPEND _qm_depends DEPENDS ${_target}_lupdate)
|
||||
else()
|
||||
if(FUNC_PREFIX)
|
||||
message(WARNING "qm_add_translation: no source files collected, PREFIX ignored")
|
||||
endif()
|
||||
|
||||
if(FUNC_TS_DIR)
|
||||
message(WARNING "qm_add_translation: no source files collected, TS_DIR ignored")
|
||||
endif()
|
||||
|
||||
if(FUNC_TS_DEPENDS)
|
||||
message(WARNING "qm_add_translation: no source files collected, TS_DEPENDS ignored")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(FUNC_QM_DIR)
|
||||
set(_qm_dir ${FUNC_QM_DIR})
|
||||
else()
|
||||
set(_qm_dir ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
set(_qm_target)
|
||||
|
||||
if(_qm_options)
|
||||
list(PREPEND _qm_options OPTIONS)
|
||||
endif()
|
||||
|
||||
_qm_add_lrelease_target(${_target}_lrelease
|
||||
INPUT ${_ts_files} ${FUNC_TS_FILES}
|
||||
DESTINATION ${_qm_dir}
|
||||
${_qm_options}
|
||||
${_qm_depends}
|
||||
)
|
||||
|
||||
add_dependencies(${_target} ${_target}_lrelease)
|
||||
|
||||
# Add release dependencies
|
||||
if(FUNC_TARGETS)
|
||||
foreach(_item IN LISTS FUNC_TARGETS)
|
||||
add_dependencies(${_item} ${_target}_lrelease)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
foreach(_item IN LISTS FUNC_QM_DEPENDS)
|
||||
add_dependencies(${_item} ${_target}_lrelease)
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# ----------------------------------
|
||||
# Private functions
|
||||
# ----------------------------------
|
||||
# Input: cxx source files
|
||||
# Output: target ts files
|
||||
function(_qm_add_lupdate_target _target)
|
||||
set(options CREATE_ONCE)
|
||||
set(oneValueArgs)
|
||||
set(multiValueArgs INPUT OUTPUT OPTIONS DEPENDS)
|
||||
|
||||
cmake_parse_arguments(_LUPDATE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
set(_lupdate_deps ${_LUPDATE_DEPENDS})
|
||||
|
||||
set(_my_sources ${_LUPDATE_INPUT})
|
||||
set(_my_tsfiles ${_LUPDATE_OUTPUT})
|
||||
|
||||
add_custom_target(${_target} DEPENDS ${_lupdate_deps})
|
||||
qm_get_executable_location(Qt${QT_VERSION_MAJOR}::lupdate _lupdate_exe)
|
||||
|
||||
set(_create_once_warning)
|
||||
set(_create_once_warning_printed off)
|
||||
|
||||
# Prepare for create once
|
||||
if(_LUPDATE_CREATE_ONCE)
|
||||
# Check if all src files are available
|
||||
foreach(_file IN LISTS _my_sources)
|
||||
get_filename_component(_abs_file ${_file} ABSOLUTE)
|
||||
|
||||
if(NOT EXISTS ${_abs_file})
|
||||
get_filename_component(_file ${_file} NAME)
|
||||
set(_create_once_warning "source file \"${_file}\" is not available, skip generating ts file now")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Check if options contain generator expressions
|
||||
if(NOT _create_once_warning)
|
||||
foreach(_opt IN LISTS _LUPDATE_OPTIONS)
|
||||
string(GENEX_STRIP "${_opt}" _no_genex)
|
||||
|
||||
if(NOT _no_genex STREQUAL _opt)
|
||||
set(_create_once_warning "lupdate options contain generator expressions, skip generating ts file now")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
foreach(_ts_file IN LISTS _my_tsfiles)
|
||||
# make a list file to call lupdate on, so we don't make our commands too
|
||||
# long for some systems
|
||||
get_filename_component(_ts_name ${_ts_file} NAME)
|
||||
set(_ts_lst_file "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${_ts_name}_lst_file")
|
||||
set(_lst_file_srcs)
|
||||
|
||||
foreach(_lst_file_src IN LISTS _my_sources)
|
||||
set(_lst_file_srcs "${_lst_file_src}\n${_lst_file_srcs}")
|
||||
endforeach()
|
||||
|
||||
get_directory_property(_inc_DIRS INCLUDE_DIRECTORIES)
|
||||
|
||||
foreach(_pro_include IN LISTS _inc_DIRS)
|
||||
get_filename_component(_abs_include "${_pro_include}" ABSOLUTE)
|
||||
set(_lst_file_srcs "-I${_pro_include}\n${_lst_file_srcs}")
|
||||
endforeach()
|
||||
|
||||
file(WRITE ${_ts_lst_file} "${_lst_file_srcs}")
|
||||
|
||||
get_filename_component(_ts_abs ${_ts_file} ABSOLUTE)
|
||||
|
||||
if(_LUPDATE_CREATE_ONCE AND NOT EXISTS ${_ts_abs})
|
||||
if(_create_once_warning)
|
||||
if(NOT _create_once_warning_printed)
|
||||
message(WARNING "qm_add_translation: ${_create_once_warning}")
|
||||
set(_create_once_warning_printed on)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Lupdate: Generating ${_ts_name}")
|
||||
get_filename_component(_abs_file ${_ts_file} ABSOLUTE)
|
||||
get_filename_component(_dir ${_abs_file} DIRECTORY)
|
||||
file(MAKE_DIRECTORY ${_dir})
|
||||
execute_process(
|
||||
COMMAND ${_lupdate_exe} ${_LUPDATE_OPTIONS} "@${_ts_lst_file}" -ts ${_ts_file}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_QUIET
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${_target} POST_BUILD
|
||||
COMMAND ${_lupdate_exe}
|
||||
ARGS ${_LUPDATE_OPTIONS} "@${_ts_lst_file}" -ts ${_ts_file}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
BYPRODUCTS ${_ts_lst_file}
|
||||
VERBATIM
|
||||
)
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# Input: ts files
|
||||
# Output: list to append qm files
|
||||
function(_qm_add_lrelease_target _target)
|
||||
set(options)
|
||||
set(oneValueArgs DESTINATION OUTPUT)
|
||||
set(multiValueArgs INPUT OPTIONS DEPENDS)
|
||||
|
||||
cmake_parse_arguments(_LRELEASE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
set(_lrelease_files ${_LRELEASE_INPUT})
|
||||
set(_lrelease_deps ${_LRELEASE_DEPENDS})
|
||||
|
||||
qm_get_executable_location(Qt${QT_VERSION_MAJOR}::lrelease _lrelease_exe)
|
||||
|
||||
set(_qm_files)
|
||||
|
||||
foreach(_file IN LISTS _lrelease_files)
|
||||
get_filename_component(_abs_FILE ${_file} ABSOLUTE)
|
||||
get_filename_component(_qm_file ${_file} NAME)
|
||||
|
||||
# everything before the last dot has to be considered the file name (including other dots)
|
||||
string(REGEX REPLACE "\\.[^.]*$" "" FILE_NAME ${_qm_file})
|
||||
get_source_file_property(output_location ${_abs_FILE} OUTPUT_LOCATION)
|
||||
|
||||
if(output_location)
|
||||
set(_out_dir ${output_location})
|
||||
elseif(_LRELEASE_DESTINATION)
|
||||
set(_out_dir ${_LRELEASE_DESTINATION})
|
||||
else()
|
||||
set(_out_dir ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
set(_qm_file "${_out_dir}/${FILE_NAME}.qm")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${_qm_file}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${_out_dir}
|
||||
COMMAND ${_lrelease_exe} ARGS ${_LRELEASE_OPTIONS} ${_abs_FILE} -qm ${_qm_file}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${_lrelease_files}
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
list(APPEND _qm_files ${_qm_file})
|
||||
endforeach()
|
||||
|
||||
add_custom_target(${_target} ALL DEPENDS ${_lrelease_deps} ${_qm_files})
|
||||
|
||||
if(_LRELEASE_OUTPUT)
|
||||
set(${_LRELEASE_OUTPUT} ${_qm_files} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
68
libs/qwindowkit/qmsetup/cmake/modules/private/Generate.cmake
Normal file
68
libs/qwindowkit/qmsetup/cmake/modules/private/Generate.cmake
Normal file
@@ -0,0 +1,68 @@
|
||||
#[[
|
||||
Warning: This module is private, may be modified or removed in the future, please use with caution.
|
||||
]] #
|
||||
|
||||
include_guard(DIRECTORY)
|
||||
|
||||
#[[
|
||||
Create the names of output files preserving relative dirs. (Ported from MOC command)
|
||||
|
||||
qm_make_output_file(<infile> <prefix> <ext> <OUT>)
|
||||
|
||||
OUT: output source file paths
|
||||
#]]
|
||||
function(qm_make_output_file _infile _prefix _ext _out)
|
||||
string(LENGTH ${CMAKE_CURRENT_BINARY_DIR} _binlength)
|
||||
string(LENGTH ${_infile} _infileLength)
|
||||
set(_checkinfile ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
if(_infileLength GREATER _binlength)
|
||||
string(SUBSTRING "${_infile}" 0 ${_binlength} _checkinfile)
|
||||
|
||||
if(_checkinfile STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
file(RELATIVE_PATH _name ${CMAKE_CURRENT_BINARY_DIR} ${_infile})
|
||||
else()
|
||||
file(RELATIVE_PATH _name ${CMAKE_CURRENT_SOURCE_DIR} ${_infile})
|
||||
endif()
|
||||
else()
|
||||
file(RELATIVE_PATH _name ${CMAKE_CURRENT_SOURCE_DIR} ${_infile})
|
||||
endif()
|
||||
|
||||
if(CMAKE_HOST_WIN32 AND _name MATCHES "^([a-zA-Z]):(.*)$") # absolute path
|
||||
set(_name "${CMAKE_MATCH_1}_${CMAKE_MATCH_2}")
|
||||
endif()
|
||||
|
||||
set(_outfile "${CMAKE_CURRENT_BINARY_DIR}/${_name}")
|
||||
string(REPLACE ".." "__" _outfile ${_outfile})
|
||||
get_filename_component(_outpath ${_outfile} PATH)
|
||||
get_filename_component(_outfile ${_outfile} NAME_WLE)
|
||||
|
||||
file(MAKE_DIRECTORY ${_outpath})
|
||||
set(${_out} ${_outpath}/${_prefix}${_outfile}.${_ext} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
#[[
|
||||
Create a custom command to run `xxd`.
|
||||
|
||||
qm_add_binary_resource(<input> <output>)
|
||||
#]]
|
||||
function(qm_add_binary_resource _input _output)
|
||||
find_program(_xxd "xxd")
|
||||
if(NOT _xxd)
|
||||
get_filename_component(_name ${_output} NAME)
|
||||
string(MAKE_C_IDENTIFIER ${_name} _name)
|
||||
set(_cmd "${CMAKE_COMMAND}"
|
||||
-D "input=${_input}"
|
||||
-D "output=${_output}"
|
||||
-D "name=${_name}"
|
||||
-P "${QMSETUP_MODULES_DIR}/scripts/xxd.cmake")
|
||||
else()
|
||||
set(_cmd "${_xxd}" "-i" "${_input}" "${_output}")
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${_output}
|
||||
COMMAND ${_cmd}
|
||||
DEPENDS ${_input}
|
||||
)
|
||||
endfunction()
|
||||
@@ -0,0 +1,158 @@
|
||||
#[[
|
||||
Warning: This module is private, may be modified or removed in the future, please use with caution.
|
||||
]] #
|
||||
|
||||
if(NOT DEFINED QMSETUP_PACKAGE_BUILD_TYPE)
|
||||
set(QMSETUP_PACKAGE_BUILD_TYPE "Release")
|
||||
endif()
|
||||
|
||||
include_guard(DIRECTORY)
|
||||
|
||||
#[[
|
||||
Install external package at configuration phase.
|
||||
|
||||
qm_install_package(<name>
|
||||
[SOURCE_DIR <dir>]
|
||||
[BUILD_TREE_DIR <dir>]
|
||||
[INSTALL_DIR <dir>]
|
||||
[CMAKE_PACKAGE_SUBDIR <subdir>]
|
||||
|
||||
[BUILD_TYPE <type>]
|
||||
[CONFIGURE_ARGS <arg...>]
|
||||
|
||||
[RESULT_PATH <VAR>]
|
||||
)
|
||||
]] #
|
||||
function(qm_install_package _name)
|
||||
set(options)
|
||||
set(oneValueArgs SOURCE_DIR BUILD_TREE_DIR INSTALL_DIR CMAKE_PACKAGE_SUBDIR BUILD_TYPE RESULT_PATH)
|
||||
set(multiValueArgs CONFIGURE_ARGS)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# Directories
|
||||
if(NOT FUNC_SOURCE_DIR)
|
||||
message(FATAL_ERROR "pre_install_package: SOURCE_DIR is not spefified")
|
||||
endif()
|
||||
|
||||
set(_src_dir ${FUNC_SOURCE_DIR})
|
||||
|
||||
if(FUNC_BUILD_TREE_DIR)
|
||||
set(_build_tree_dir ${FUNC_BUILD_TREE_DIR})
|
||||
else()
|
||||
set(_build_tree_dir ${CMAKE_BINARY_DIR}/_build)
|
||||
endif()
|
||||
|
||||
if(FUNC_INSTALL_DIR)
|
||||
set(_install_dir ${FUNC_INSTALL_DIR})
|
||||
else()
|
||||
set(_install_dir ${CMAKE_BINARY_DIR}/_install)
|
||||
endif()
|
||||
|
||||
if(FUNC_CMAKE_PACKAGE_SUBDIR)
|
||||
set(_cmake_subdir ${FUNC_CMAKE_PACKAGE_SUBDIR})
|
||||
else()
|
||||
include(GNUInstallDirs)
|
||||
set(_cmake_subdir "${CMAKE_INSTALL_LIBDIR}/cmake/${_name}")
|
||||
endif()
|
||||
|
||||
# Build types
|
||||
if(FUNC_BUILD_TYPE)
|
||||
set(_build_type -DCMAKE_BUILD_TYPE=${FUNC_BUILD_TYPE})
|
||||
elseif(CMAKE_BUILD_TYPE)
|
||||
set(_build_type -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE})
|
||||
elseif(NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(_build_type -DCMAKE_BUILD_TYPE=${QMSETUP_PACKAGE_BUILD_TYPE})
|
||||
else()
|
||||
set(_build_type)
|
||||
endif()
|
||||
|
||||
if(FUNC_BUILD_TYPE)
|
||||
set(_build_types ${FUNC_BUILD_TYPE})
|
||||
else()
|
||||
set(_build_types ${QMSETUP_PACKAGE_BUILD_TYPE})
|
||||
endif()
|
||||
|
||||
# Do it
|
||||
set(_install_cmake_dir ${_install_dir}/${_cmake_subdir})
|
||||
set(_build_dir ${_build_tree_dir}/${_name})
|
||||
|
||||
if(NOT IS_DIRECTORY ${_install_cmake_dir})
|
||||
# Determine generator
|
||||
set(_extra_args)
|
||||
|
||||
if(CMAKE_GENERATOR)
|
||||
set(_extra_args -G "${CMAKE_GENERATOR}")
|
||||
endif()
|
||||
|
||||
if(CMAKE_GENERATOR_PLATFORM)
|
||||
set(_extra_args -A "${CMAKE_GENERATOR_PLATFORM}")
|
||||
endif()
|
||||
|
||||
# Remove old build directory
|
||||
if(IS_DIRECTORY ${_build_dir})
|
||||
file(REMOVE_RECURSE ${_build_dir})
|
||||
endif()
|
||||
|
||||
file(MAKE_DIRECTORY ${_build_tree_dir})
|
||||
|
||||
# Configure
|
||||
message(STATUS "Configuring ${_name}...")
|
||||
set(_log_file ${_build_tree_dir}/${_name}_configure.log)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -S ${_src_dir} -B ${_build_dir}
|
||||
${_extra_args} ${_build_type}
|
||||
# Pass through CMAKE_INSTALL_LIBDIR to ensure the package uses the same
|
||||
# lib directory convention (e.g., lib vs lib64) as the parent project.
|
||||
# Without this, packages using GNUInstallDirs may default to a different
|
||||
# directory structure than expected.
|
||||
"-DCMAKE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR}"
|
||||
"-DCMAKE_INSTALL_PREFIX=${_install_dir}" ${FUNC_CONFIGURE_ARGS}
|
||||
OUTPUT_FILE ${_log_file}
|
||||
ERROR_FILE ${_log_file}
|
||||
RESULT_VARIABLE _code
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
if(NOT ${_code} EQUAL 0)
|
||||
message(FATAL_ERROR "Configure failed, check \"${_log_file}\"")
|
||||
endif()
|
||||
|
||||
# Build
|
||||
foreach(_item IN LISTS _build_types)
|
||||
message(STATUS "Building ${_name} (${_item})...")
|
||||
set(_log_file ${_build_tree_dir}/${_name}_build-${_item}.log)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} --build ${_build_dir} --config ${_item} --parallel
|
||||
OUTPUT_FILE ${_log_file}
|
||||
ERROR_FILE ${_log_file}
|
||||
RESULT_VARIABLE _code
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
if(NOT ${_code} EQUAL 0)
|
||||
message(FATAL_ERROR "Build failed, check \"${_log_file}\"")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Install
|
||||
foreach(_item IN LISTS _build_types)
|
||||
message(STATUS "Installing ${_name} (${_item})...")
|
||||
set(_log_file ${_build_tree_dir}/${_name}_install-${_item}.log)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} --build ${_build_dir} --config ${_item} --target install
|
||||
OUTPUT_FILE ${_log_file}
|
||||
ERROR_FILE ${_log_file}
|
||||
RESULT_VARIABLE _code
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
if(NOT ${_code} EQUAL 0)
|
||||
message(FATAL_ERROR "Install failed, check \"${_log_file}\"")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(FUNC_RESULT_PATH)
|
||||
set(${FUNC_RESULT_PATH} ${_install_cmake_dir} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,37 @@
|
||||
include_guard(DIRECTORY)
|
||||
|
||||
function(qm_get_windows_proxy _out)
|
||||
if(NOT WIN32)
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND reg query "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable
|
||||
OUTPUT_VARIABLE _proxy_enable_output
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
if(NOT _proxy_enable_output MATCHES "ProxyEnable[ \t\r\n]+REG_DWORD[ \t\r\n]+0x1")
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND reg query "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyServer
|
||||
OUTPUT_VARIABLE _proxy_server_output
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
if(NOT _proxy_server_output MATCHES "ProxyServer[ \t\r\n]+REG_SZ[ \t\r\n]+(.*)")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(${_out} ${CMAKE_MATCH_1} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
macro(qm_set_proxy_env _proxy)
|
||||
set(ENV{HTTP_PROXY} "http://${_proxy}")
|
||||
set(ENV{HTTPS_PROXY} "http://${_proxy}")
|
||||
set(ENV{ALL_PROXY} "http://${_proxy}")
|
||||
endmacro()
|
||||
19
libs/qwindowkit/qmsetup/cmake/scripts/configure_file.cmake
Normal file
19
libs/qwindowkit/qmsetup/cmake/scripts/configure_file.cmake
Normal file
@@ -0,0 +1,19 @@
|
||||
# MIT License
|
||||
# Copyright (c) 2023 SineStriker
|
||||
|
||||
# Description:
|
||||
# Command that executes `configure_file`.
|
||||
|
||||
# Usage:
|
||||
# cmake
|
||||
# -D input=<file>
|
||||
# -D output=<file>
|
||||
# -D force=TRUE/FALSE
|
||||
# [-D args=<args...>]
|
||||
# -P configure_file.cmake
|
||||
|
||||
if(force AND EXISTS ${_output})
|
||||
file(REMOVE ${_output})
|
||||
endif()
|
||||
|
||||
configure_file(${input} ${output} ${args})
|
||||
64
libs/qwindowkit/qmsetup/cmake/scripts/copy.cmake
Normal file
64
libs/qwindowkit/qmsetup/cmake/scripts/copy.cmake
Normal file
@@ -0,0 +1,64 @@
|
||||
# MIT License
|
||||
# Copyright (c) 2023 SineStriker
|
||||
|
||||
# Description:
|
||||
# Copy file or directory to destination if different.
|
||||
# Mainly use `file(INSTALL)` to implement.
|
||||
|
||||
# Usage:
|
||||
# cmake
|
||||
# -D src=<files/dirs...>
|
||||
# -D dest=<dir>
|
||||
# [-D dest_base=<dir>]
|
||||
# [-D args=<args...>]
|
||||
# -P copy.cmake
|
||||
|
||||
# Check required arguments
|
||||
if(NOT src)
|
||||
message(FATAL_ERROR "src not defined.")
|
||||
endif()
|
||||
|
||||
if(NOT dest)
|
||||
message(FATAL_ERROR "dest not defined.")
|
||||
endif()
|
||||
|
||||
# Calculate destination
|
||||
if(dest_base)
|
||||
set(_dest_base ${dest_base})
|
||||
else()
|
||||
set(_dest_base ${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
get_filename_component(_dest ${dest} ABSOLUTE BASE_DIR ${_dest_base})
|
||||
|
||||
# Copy
|
||||
foreach(_file IN LISTS src)
|
||||
# Avoid using `get_filename_component` to keep the trailing slash
|
||||
set(_path ${_file})
|
||||
|
||||
if(NOT IS_ABSOLUTE ${_path})
|
||||
set(_path "${CMAKE_BINARY_DIR}/${_path}")
|
||||
endif()
|
||||
|
||||
if(IS_DIRECTORY ${_path})
|
||||
file(INSTALL DESTINATION ${_dest}
|
||||
TYPE DIRECTORY
|
||||
FILES ${_path}
|
||||
${args}
|
||||
)
|
||||
else()
|
||||
set(_paths)
|
||||
|
||||
if(${_path} MATCHES "\\*\\*")
|
||||
file(GLOB_RECURSE _paths ${_path})
|
||||
else()
|
||||
file(GLOB _paths ${_path})
|
||||
endif()
|
||||
|
||||
file(INSTALL DESTINATION ${_dest}
|
||||
TYPE FILE
|
||||
FILES ${_paths}
|
||||
${args}
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
245
libs/qwindowkit/qmsetup/cmake/scripts/unixdeps.sh
Normal file
245
libs/qwindowkit/qmsetup/cmake/scripts/unixdeps.sh
Normal file
@@ -0,0 +1,245 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MIT License
|
||||
# Copyright (c) 2023 SineStriker
|
||||
|
||||
# Description: This script calls `qmcorecmd` to deploy dependencies on Mac/Linux.
|
||||
|
||||
# Show usage
|
||||
usage() {
|
||||
echo "Usage: $(basename $0) -i <dir> -m <path>"
|
||||
echo " --plugindir <plugin_dir> --libdir <lib_dir> --qmldir <qml_dir>"
|
||||
echo " [--qmake <qmake_path>] [--extra <extra_path>]..."
|
||||
echo " [--qml <qml_module>]... [--plugin <plugin>]... [--copy <src> <dest>]..."
|
||||
echo " [-f] [-s] [-V] [-h]"
|
||||
echo " -i <input_dir> Directory containing binaries and libraries"
|
||||
echo " -m <corecmd_path> Path to corecmd"
|
||||
echo " --plugindir <plugin_dir> Output directory for plugins"
|
||||
echo " --libdir <lib_dir> Output directory for libraries"
|
||||
echo " --qmldir <qml_dir> Output directory for QML files"
|
||||
echo " --qmake <qmake_path> Path to qmake (optional)"
|
||||
echo " --extra <extra_path> Extra plugin searching path (repeatable)"
|
||||
echo " --qml <qml_module> Relative path to QML directory (repeatable)"
|
||||
echo " --plugin <plugin> Specify a Qt plugin to deploy (repeatable)"
|
||||
echo " --copy <src> <dest> Specify additional binary file to copy and its destination directory (repeatable)"
|
||||
echo " --linkdirs-file Add library searching paths from a list file"
|
||||
echo " -L Add a library searching path"
|
||||
echo " -f Force overwrite existing files"
|
||||
echo " -s Ignore C/C++ runtime and system libraries"
|
||||
echo " -V Show verbose output"
|
||||
echo " -h Show this help message"
|
||||
}
|
||||
|
||||
# Initialize arguments
|
||||
EXTRA_PLUGIN_PATHS=()
|
||||
QML_REL_PATHS=()
|
||||
ARGS=()
|
||||
VERBOSE=""
|
||||
PLUGINS=()
|
||||
FILES=""
|
||||
|
||||
# Parse command line
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
-i) INPUT_DIR="$2"; shift 2;;
|
||||
-m) CORECMD_PATH="$2"; shift 2;;
|
||||
-L) ARGS+=("-L \"$2\""); shift 2;;
|
||||
--linkdirs-file) ARGS+=("--linkdirs-file \"$2\""); shift 2;;
|
||||
--plugindir) PLUGIN_DIR="$2"; shift 2;;
|
||||
--libdir) LIB_DIR="$2"; shift 2;;
|
||||
--qmldir) QML_DIR="$2"; shift 2;;
|
||||
--qmake) QMAKE_PATH="$2"; shift 2;;
|
||||
--extra) EXTRA_PLUGIN_PATHS+=("$2"); shift 2;;
|
||||
--plugin) PLUGINS+=("$2"); shift 2;;
|
||||
--qml) QML_REL_PATHS+=("$2"); shift 2;;
|
||||
--copy) ARGS+=("-c \"$2\" \"$3\""); shift 3;;
|
||||
-f|-s) ARGS+=("$1"); shift;;
|
||||
-V) VERBOSE="-V"; shift;;
|
||||
-h) usage; exit 0;;
|
||||
*) echo "Error: Unsupported argument $1"; usage; exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check required arguments
|
||||
for arg in INPUT_DIR PLUGIN_DIR LIB_DIR QML_DIR CORECMD_PATH; do
|
||||
if [[ -z ${!arg} ]]; then
|
||||
echo "Error: Missing required argument '$arg'"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Get Qt plugin and QML paths
|
||||
PLUGIN_PATHS=()
|
||||
QML_PATH=""
|
||||
if [[ -n "$QMAKE_PATH" ]]; then
|
||||
QMAKE_PLUGIN_PATH=$($QMAKE_PATH -query QT_INSTALL_PLUGINS)
|
||||
PLUGIN_PATHS+=("$QMAKE_PLUGIN_PATH")
|
||||
|
||||
QML_PATH=$($QMAKE_PATH -query QT_INSTALL_QML)
|
||||
fi
|
||||
|
||||
# Add extra plugin searching paths
|
||||
PLUGIN_PATHS+=("${EXTRA_PLUGIN_PATHS[@]}")
|
||||
|
||||
# Ensure that the QML search path is not empty
|
||||
# when the QML related path is specified (qmake needs to be specified)
|
||||
if [[ ${#QML_REL_PATHS[@]} -gt 0 && -z "$QML_PATH" ]]; then
|
||||
echo "Error: qmake path must be specified when QML paths are provided"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Search input directory
|
||||
search_input_dir() {
|
||||
local path="$1"
|
||||
for item in "$path"/*; do
|
||||
if [ -d "$item" ]; then
|
||||
# Check if the directory is mac .framework
|
||||
if [[ "OSTYPE" == "darwin"* ]] && [[ "$item" == *.framework ]]; then
|
||||
FILES="$FILES \"$item\""
|
||||
else
|
||||
search_input_dir "$item"
|
||||
fi
|
||||
elif [ -f "$item" ]; then
|
||||
if [[ "$OSTYPE" == "msys"* ]] || [[ "$OSTYPE" == "win32" ]] || [[ "$OSTYPE" == "cygwin"* ]]; then
|
||||
# On Windows, search for.exe and.dll files
|
||||
FILES="$FILES \"$item\""
|
||||
else
|
||||
# On Unix, traverse all files, using the file command to
|
||||
# check for executable binary files
|
||||
file_type=$(file -b "$item")
|
||||
if [[ ($file_type == "ELF"* || $file_type == "Mach-O"*) && -x "$item" ]]; then
|
||||
FILES="$FILES \"$item\""
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
search_input_dir "$INPUT_DIR"
|
||||
|
||||
# Find the full path to the Qt plugin
|
||||
for plugin_path in "${PLUGINS[@]}"; do
|
||||
# Check format
|
||||
if [[ $plugin_path == */* ]]; then
|
||||
IFS='/' read -r -a plugin_parts <<< "$plugin_path"
|
||||
|
||||
# Extracts the category and name
|
||||
category=${plugin_parts[0]}
|
||||
name=${plugin_parts[1]}
|
||||
|
||||
# Calculate destination directory
|
||||
dest_dir="${PLUGIN_DIR}/${category}"
|
||||
|
||||
# Initialize an array to store found plugins
|
||||
found_plugins=""
|
||||
|
||||
# Traverse the path and find the specific plug-in files
|
||||
for search_path in "${PLUGIN_PATHS[@]}"; do
|
||||
while IFS= read -r plugin; do
|
||||
# Get name
|
||||
plugin_name=$(basename "$plugin")
|
||||
|
||||
# Check if the plugin was already found to avoid duplicates
|
||||
if [[ ! $found_plugins =~ $plugin_name ]]; then
|
||||
found_plugins+="$plugin_name "
|
||||
ARGS+=("-c \"$plugin\" \"$dest_dir\"")
|
||||
fi
|
||||
done < <(find "${search_path}/${category}" -name "lib${name}.*" ! -name "*debug*" ! -type d -print)
|
||||
done
|
||||
|
||||
if [ ${#found_plugins[@]} -eq 0 ]; then
|
||||
echo "Error: Plugin '${plugin_path}' not found in any search paths."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Error: Invalid plugin format '${plugin_path}'. Expected format: <category>/<name>"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Process QML item
|
||||
handle_qml_file() {
|
||||
local file="$1"
|
||||
local rel_path="${file#$QML_PATH/}"
|
||||
|
||||
local target="$QML_DIR/$rel_path"
|
||||
local target_dir="$(dirname "$target")"
|
||||
|
||||
# Directory: must be mac framework
|
||||
if [ -d "$file" ]; then
|
||||
ARGS+=("-c \"$file\" \"$target_dir\"")
|
||||
return
|
||||
fi
|
||||
|
||||
# Ignore specific files
|
||||
if [[ "$OSTYPE" == "msys"* ]] || [[ "$OSTYPE" == "win32" ]] || [[ "$OSTYPE" == "cygwin"* ]]; then
|
||||
if [[ "$file" == *.pdb ]] || [[ "$file" == *d.dll ]]; then
|
||||
return
|
||||
fi
|
||||
else
|
||||
if [[ "$file" == *_debug.dylib ]] || [[ "$file" == *.so.debug ]]; then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
# Determine whether it is an executable binary file and handle it accordingly
|
||||
if [[ "$OSTYPE" == "msys"* ]] || [[ "$OSTYPE" == "win32" ]] || [[ "$OSTYPE" == "cygwin"* ]]; then
|
||||
if [[ "$file" == *.dll || "$file" == *.exe ]]; then
|
||||
ARGS+=("-c \"$file\" \"$target_dir\"")
|
||||
else
|
||||
mkdir -p "$target_dir"
|
||||
cp "$file" "$target"
|
||||
fi
|
||||
else
|
||||
file_type=$(file -b "$file")
|
||||
if [[ ($file_type == "ELF"* || $file_type == "Mach-O"*) && -x "$file" ]]; then
|
||||
ARGS+=("-c \"$file\" \"$target_dir\"")
|
||||
else
|
||||
mkdir -p "$target_dir"
|
||||
cp "$file" "$target"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Search QML directory
|
||||
search_qml_dir() {
|
||||
local path="$1"
|
||||
for item in "$path"/*; do
|
||||
if [ -d "$item" ]; then
|
||||
# Check if the path is mac .framework
|
||||
if [[ "OSTYPE" == "darwin"* ]] && [[ "$item" == *.framework ]]; then
|
||||
handle_qml_file "$item"
|
||||
else
|
||||
search_qml_dir "$item"
|
||||
fi
|
||||
elif [ -f "$item" ]; then
|
||||
handle_qml_file "$item"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Process QML directories
|
||||
for qml_rel_path in "${QML_REL_PATHS[@]}"; do
|
||||
full_path="$QML_PATH/$qml_rel_path"
|
||||
if [[ -d "$full_path" ]]; then
|
||||
# Directory
|
||||
search_qml_dir "$full_path"
|
||||
elif [[ -f "$full_path" ]]; then
|
||||
# File
|
||||
handle_qml_file "$full_path" "$QML_DIR"
|
||||
fi
|
||||
done
|
||||
|
||||
# Build and execute the deploy command
|
||||
DEPLOY_CMD="$CORECMD_PATH deploy $FILES ${ARGS[@]} -o \"$LIB_DIR\" $VERBOSE"
|
||||
if [[ "$VERBOSE" == "-V" ]]; then
|
||||
echo "Executing: $DEPLOY_CMD"
|
||||
fi
|
||||
eval $DEPLOY_CMD
|
||||
|
||||
# Check the deployment result
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
294
libs/qwindowkit/qmsetup/cmake/scripts/windeps.bat
Normal file
294
libs/qwindowkit/qmsetup/cmake/scripts/windeps.bat
Normal file
@@ -0,0 +1,294 @@
|
||||
:: MIT License
|
||||
:: Copyright (c) 2023 SineStriker
|
||||
|
||||
:: Description: This script calls `qmcorecmd` to deploy dependencies on Windows.
|
||||
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
:: Initialize arguments
|
||||
set "INPUT_DIR="
|
||||
set "PLUGIN_DIR="
|
||||
set "LIB_DIR="
|
||||
set "QML_DIR="
|
||||
set "QMAKE_PATH="
|
||||
set "CORECMD_PATH="
|
||||
set "VERBOSE="
|
||||
set "FILES="
|
||||
set "EXTRA_PLUGIN_PATHS="
|
||||
set "PLUGINS=" & set /a "PLUGIN_COUNT=0"
|
||||
set "QML_REL_PATHS="
|
||||
set "ARGS=" & set /a "ARG_COUNT=0"
|
||||
|
||||
:: Parse command line
|
||||
:parse_args
|
||||
if "%~1"=="" goto :end_parse_args
|
||||
if "%1"=="-i" set "INPUT_DIR=%~2" & shift & shift & goto :parse_args
|
||||
if "%1"=="-m" set "CORECMD_PATH=%~2" & shift & shift & goto :parse_args
|
||||
if "%1"=="--plugindir" set "PLUGIN_DIR=%~2" & shift & shift & goto :parse_args
|
||||
if "%1"=="--libdir" set "LIB_DIR=%~2" & shift & shift & goto :parse_args
|
||||
if "%1"=="--qmldir" set "QML_DIR=%~2" & shift & shift & goto :parse_args
|
||||
if "%1"=="--qmake" set "QMAKE_PATH=%~2" & shift & shift & goto :parse_args
|
||||
if "%1"=="--extra" set "EXTRA_PLUGIN_PATHS=!EXTRA_PLUGIN_PATHS! %~2" & shift & shift & goto :parse_args
|
||||
if "%1"=="--plugin" set /a "PLUGIN_COUNT+=1" & set "PLUGINS[!PLUGIN_COUNT!]=%~2" & shift & shift & goto :parse_args
|
||||
if "%1"=="--qml" set "QML_REL_PATHS=!QML_REL_PATHS! %~2" & shift & shift & goto :parse_args
|
||||
if "%1"=="--copy" call :push_args -c %~2 %~3 & shift & shift & shift & goto :parse_args
|
||||
if "%1"=="-f" call :push_args -f & shift & goto :parse_args
|
||||
if "%1"=="-s" call :push_args -s & shift & goto :parse_args
|
||||
if "%1"=="-V" set "VERBOSE=-V" & shift & goto :parse_args
|
||||
if "%1"=="-h" call :usage & exit /b
|
||||
|
||||
if "%1"=="--linkdirs-file" call :push_args --linkdirs-file %~2 & shift & shift & goto :parse_args
|
||||
if "%1"=="-L" call :push_args -L %~2 & shift & shift & goto :parse_args
|
||||
|
||||
shift
|
||||
goto :parse_args
|
||||
:end_parse_args
|
||||
|
||||
:: Check required arguments
|
||||
if not defined INPUT_DIR echo Error: Missing required argument 'INPUT_DIR' & call :usage & exit /b
|
||||
if not defined PLUGIN_DIR echo Error: Missing required argument 'PLUGIN_DIR' & call :usage & exit /b
|
||||
if not defined LIB_DIR echo Error: Missing required argument 'LIB_DIR' & call :usage & exit /b
|
||||
if not defined QML_DIR echo Error: Missing required argument 'QML_DIR' & call :usage & exit /b
|
||||
if not defined CORECMD_PATH echo Error: Missing required argument 'CORECMD_PATH' & call :usage & exit /b
|
||||
|
||||
:: Normalize
|
||||
set "INPUT_DIR=!INPUT_DIR:/=\!"
|
||||
set "PLUGIN_DIR=!PLUGIN_DIR:/=\!"
|
||||
set "LIB_DIR=!LIB_DIR:/=\!"
|
||||
set "QML_DIR=!QML_DIR:/=\!"
|
||||
set "CORECMD_PATH=!CORECMD_PATH:/=\!"
|
||||
|
||||
:: Get Qt plugin and QML paths
|
||||
set "PLUGIN_PATHS="
|
||||
set "QML_PATH="
|
||||
if defined QMAKE_PATH (
|
||||
for /f "tokens=*" %%a in ('!QMAKE_PATH! -query QT_INSTALL_PLUGINS') do set "QMAKE_PLUGIN_PATH=%%a"
|
||||
set "PLUGIN_PATHS=!QMAKE_PLUGIN_PATH!"
|
||||
for /f "tokens=*" %%a in ('!QMAKE_PATH! -query QT_INSTALL_QML') do set "QML_PATH=%%a"
|
||||
set "QML_PATH=!QML_PATH:/=\!"
|
||||
|
||||
:: Add Qt bin directory
|
||||
for /f "tokens=*" %%a in ('!QMAKE_PATH! -query QT_INSTALL_BINS') do set "QT_BIN_PATH=%%a"
|
||||
call :push_args -L !QT_BIN_PATH!
|
||||
)
|
||||
|
||||
:: Add extra plugin searching paths
|
||||
set "PLUGIN_PATHS=!PLUGIN_PATHS! !EXTRA_PLUGIN_PATHS!"
|
||||
|
||||
:: Ensure that the QML search path is not empty
|
||||
:: when the QML related path is specified (qmake needs to be specified)
|
||||
if not "%QML_REL_PATHS%"=="" (
|
||||
if "%QML_PATH%"=="" (
|
||||
echo Error: qmake path must be specified when QML paths are provided
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
:: The type of file to be searched depends on the operating system
|
||||
:: On Windows, search for.exe and.dll files
|
||||
for /r "%INPUT_DIR%" %%f in (*.exe *.dll) do (
|
||||
set "FILES=!FILES! %%f"
|
||||
)
|
||||
|
||||
:: Find the full path to the Qt plugin
|
||||
for /L %%i in (1,1,%PLUGIN_COUNT%) do (
|
||||
set "plugin_path=!PLUGINS[%%i]!"
|
||||
|
||||
:: Check format
|
||||
echo !plugin_path! | findstr /R "[^/]*\/[^/]*" >nul
|
||||
if errorlevel 1 (
|
||||
echo Error: Invalid plugin format '!plugin_path!'. Expected format: ^<category^>/^<name^>
|
||||
exit /b
|
||||
)
|
||||
|
||||
:: Extracts the category and name
|
||||
for /f "tokens=1,2 delims=/" %%a in ("!plugin_path!") do (
|
||||
set "category=%%a"
|
||||
set "name=%%b"
|
||||
|
||||
:: Calculate destination directory
|
||||
set "DESTINATION_DIR=!PLUGIN_DIR!\!category!"
|
||||
set "DESTINATION_DIR=!DESTINATION_DIR:/=\!"
|
||||
|
||||
:: Traverse the path and find the specific plug-in file
|
||||
set "FOUND_PLUGINS="
|
||||
call :search_plugin
|
||||
if not defined FOUND_PLUGINS (
|
||||
echo Error: Plugin '!plugin_path!' not found in any search paths.
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
:: Process QML directories
|
||||
for %%q in (%QML_REL_PATHS%) do (
|
||||
call :search_qml_dir "%%q"
|
||||
)
|
||||
|
||||
:: Build and execute the deploy
|
||||
set "ARGS_FILE=windeps_args.txt"
|
||||
if exist "%ARGS_FILE%" del %ARGS_FILE%
|
||||
for %%a in (!FILES!) do (
|
||||
echo %%a >> %ARGS_FILE%
|
||||
)
|
||||
for /L %%i in (1,1,%ARG_COUNT%) do (
|
||||
echo !ARGS[%%i]! >> %ARGS_FILE%
|
||||
)
|
||||
echo -o >> %ARGS_FILE%
|
||||
echo !LIB_DIR! >> %ARGS_FILE%
|
||||
echo !VERBOSE! >> %ARGS_FILE%
|
||||
set "DEPLOY_CMD=!CORECMD_PATH! deploy @!ARGS_FILE!"
|
||||
call !DEPLOY_CMD!
|
||||
|
||||
:: Check the deployment result
|
||||
if %errorlevel% neq 0 exit /b
|
||||
del %ARGS_FILE%
|
||||
exit /b
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
:: ----------------------------------------------------------------------------------
|
||||
:: Add args
|
||||
:push_args
|
||||
for %%x in (%*) do set /a "ARG_COUNT+=1" & set "ARGS[!ARG_COUNT!]=%%x"
|
||||
exit /b
|
||||
:: ----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
:: ----------------------------------------------------------------------------------
|
||||
:: Search plugins
|
||||
:search_plugin
|
||||
for %%d in (!PLUGIN_PATHS!) do (
|
||||
for %%f in ("%%d\!category!\!name!.dll") do (
|
||||
if exist "%%f" (
|
||||
call :check_debug "%%f"
|
||||
if "!ok!"=="0" (
|
||||
call :add_plugin "%%f"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
exit /b
|
||||
:: ----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
:: ----------------------------------------------------------------------------------
|
||||
:: Add plugin if not already found
|
||||
:add_plugin
|
||||
set "plugin=%~1"
|
||||
set "plugin_name=%~nx1"
|
||||
for %%i in (!FOUND_PLUGINS!) do (
|
||||
if "%%i"=="!plugin_name!" (
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
set "FOUND_PLUGINS=!FOUND_PLUGINS! !plugin_name!"
|
||||
call :push_args -c !plugin! !DESTINATION_DIR!
|
||||
exit /b
|
||||
:: ----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
:: ----------------------------------------------------------------------------------
|
||||
:: Search QML directory
|
||||
:search_qml_dir
|
||||
set "full_path=%QML_PATH%\%~1"
|
||||
if exist "%full_path%\" (
|
||||
:: Directory
|
||||
for /r "%full_path%" %%f in (*) do (
|
||||
call :handle_qml_file "%%f"
|
||||
)
|
||||
) else if exist "%full_path%" (
|
||||
:: File
|
||||
call :handle_qml_file "%full_path%"
|
||||
)
|
||||
exit /b
|
||||
:: ----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
:: ----------------------------------------------------------------------------------
|
||||
:: Check debug version of a dll
|
||||
:check_debug
|
||||
set "ok=1"
|
||||
set "file_path=%~1"
|
||||
if "!file_path:~-4!"==".pdb" exit /b
|
||||
if "!file_path:~-10!"==".dll.debug" exit /b
|
||||
if "!file_path:~-5!" == "d.dll" (
|
||||
set "prefix=!file_path:~0,-5!"
|
||||
if exist "!prefix!.dll" (
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
set "ok=0"
|
||||
exit /b
|
||||
:: ----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
:: ----------------------------------------------------------------------------------
|
||||
:: Copy or add to a deployment command
|
||||
:handle_qml_file
|
||||
set "file=%~1"
|
||||
set "file=!file:/=\!"
|
||||
|
||||
:: Ignore specific files (example)
|
||||
call :check_debug "%file%"
|
||||
if "!ok!"=="1" (
|
||||
exit /b
|
||||
)
|
||||
|
||||
:: Computes target file and folder in a very stupid way
|
||||
set "rel_path=!file:%QML_PATH%\=!"
|
||||
set "target=%QML_DIR%\%rel_path%"
|
||||
for %%I in ("!file!") do (
|
||||
set "file_dir=%%~dpI"
|
||||
)
|
||||
set "rel_dir_path=!file_dir:%QML_PATH%\=!"
|
||||
set "target_dir=%QML_DIR%\%rel_dir_path%"
|
||||
|
||||
:: Determine whether it is an executable binary file and handle it accordingly
|
||||
if "%file:~-4%"==".dll" (
|
||||
call :push_args -c !file! !target_dir!
|
||||
) else if "%file:~-4%"==".exe" (
|
||||
call :push_args -c !file! !target_dir!
|
||||
) else (
|
||||
if not exist "%target%" (
|
||||
mkdir "%target_dir%" >nul 2>&1
|
||||
)
|
||||
copy /Y "%file%" "%target%" >nul 2>&1
|
||||
)
|
||||
|
||||
exit /b
|
||||
:: ----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
:: ----------------------------------------------------------------------------------
|
||||
:: Show usage
|
||||
:usage
|
||||
echo Usage: %~n0 -i ^<dir^> -m ^<path^>
|
||||
echo --plugindir ^<plugin_dir^> --libdir ^<lib_dir^> --qmldir ^<qml_dir^>
|
||||
echo [--qmake ^<qmake_path^>] [--extra ^<extra_path^>]...
|
||||
echo [--qml ^<qml_module^>]... [--plugin ^<plugin^>]... [--copy ^<src^> ^<dest^>]...
|
||||
echo [--linkdirs-file ^<file^>]... [-L ^<path^>]...
|
||||
echo [-f] [-s] [-V] [-h]
|
||||
exit /b
|
||||
:: ----------------------------------------------------------------------------------
|
||||
23
libs/qwindowkit/qmsetup/cmake/scripts/xxd.cmake
Normal file
23
libs/qwindowkit/qmsetup/cmake/scripts/xxd.cmake
Normal file
@@ -0,0 +1,23 @@
|
||||
if(NOT DEFINED input)
|
||||
message(FATAL_ERROR "input not defined")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED output)
|
||||
message(FATAL_ERROR "output not defined")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED name)
|
||||
message(FATAL_ERROR "name not defined")
|
||||
endif()
|
||||
|
||||
get_filename_component(_output_dir ${output} DIRECTORY)
|
||||
if(NOT EXISTS ${_output_dir})
|
||||
file(MAKE_DIRECTORY ${_output_dir})
|
||||
endif()
|
||||
|
||||
file(READ ${input} _file_content HEX)
|
||||
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " _hex_content ${_file_content})
|
||||
file(WRITE ${output}
|
||||
"unsigned char ${name}[] = {${_hex_content}};\n"
|
||||
"unsigned int ${name}_len = sizeof(${name});\n"
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
Set oWS = WScript.CreateObject("WScript.Shell")
|
||||
sLinkFile = "@SHORTCUT_PATH@"
|
||||
Set oLink = oWS.CreateShortcut(sLinkFile)
|
||||
oLink.TargetPath = "@SHORTCUT_TARGET_PATH@"
|
||||
oLink.WorkingDirectory = "@SHORTCUT_WORKING_DIRECOTRY@"
|
||||
oLink.Description = "@SHORTCUT_DESCRIPTION@"
|
||||
oLink.IconLocation = "@SHORTCUT_ICON_LOCATION@"
|
||||
oLink.Save
|
||||
@@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<description>@MANIFEST_DESCRIPTION@</description>
|
||||
<assemblyIdentity
|
||||
version="@MANIFEST_VERSION@"
|
||||
name="@MANIFEST_IDENTIFIER@"
|
||||
type="win32"
|
||||
/>
|
||||
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
@MANIFEST_PRIVILEGES@
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- XAML Island won't work without the maxversiontested list -->
|
||||
<!-- Windows 10 Version 1809 (October 2018 Update) -->
|
||||
<maxversiontested Id="10.0.17763.0"/>
|
||||
<!-- Windows 10 Version 1903 (May 2019 Update) -->
|
||||
<maxversiontested Id="10.0.18362.0"/>
|
||||
<!-- Windows 10 Version 1909 (November 2019 Update) -->
|
||||
<maxversiontested Id="10.0.18363.0"/>
|
||||
<!-- Windows 10 Version 2004 (May 2020 Update) -->
|
||||
<maxversiontested Id="10.0.19041.0"/>
|
||||
<!-- Windows 10 Version 20H2 (October 2020 Update) -->
|
||||
<maxversiontested Id="10.0.19042.0"/>
|
||||
<!-- Windows 10 Version 21H1 (May 2021 Update) -->
|
||||
<maxversiontested Id="10.0.19043.0"/>
|
||||
<!-- Windows 10 Version 21H2 (November 2021 Update) -->
|
||||
<maxversiontested Id="10.0.19044.0"/>
|
||||
<!-- Windows 10 Version 22H2 -->
|
||||
<maxversiontested Id="10.0.19045.0"/>
|
||||
<!-- Windows 11 Version 21H2 -->
|
||||
<maxversiontested Id="10.0.22000.0"/>
|
||||
<!-- Windows 11 Version 22H2 -->
|
||||
<maxversiontested Id="10.0.22621.0"/>
|
||||
<!-- Windows 11 Version 23H2 -->
|
||||
<maxversiontested Id="10.0.22631.0"/>
|
||||
<!-- Windows 11 Version 24H2 -->
|
||||
<maxversiontested Id="10.0.26100.0"/>
|
||||
<!-- Windows 11 Version 25H2 -->
|
||||
<maxversiontested Id="10.0.26200.0"/>
|
||||
<!-- Windows Vista and Windows Server 2008 -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
<!-- Windows 7 and Windows Server 2008 R2 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!-- Windows 8 and Windows Server 2012 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Windows 8.1 and Windows Server 2012 R2 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows 10 and Windows 11 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
|
||||
<printerDriverIsolation xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">True</printerDriverIsolation>
|
||||
<disableWindowFiltering xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">True</disableWindowFiltering>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
|
||||
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">True</longPathAware>
|
||||
<heapType xmlns="http://schemas.microsoft.com/SMI/2020/WindowsSettings">SegmentHeap</heapType>
|
||||
@MANIFEST_UTF8@
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
29
libs/qwindowkit/qmsetup/cmake/windows/WinResource.rc.in
Normal file
29
libs/qwindowkit/qmsetup/cmake/windows/WinResource.rc.in
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef VS_VERSION_INFO
|
||||
#define VS_VERSION_INFO 1
|
||||
#endif
|
||||
|
||||
@RC_ICON_COMMENT@ IDI_ICON1 ICON DISCARDABLE "@RC_ICON_PATH@"
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION @RC_VERSION@
|
||||
PRODUCTVERSION @RC_VERSION@
|
||||
{
|
||||
BLOCK "StringFileInfo"
|
||||
{
|
||||
// U.S. English - Windows, Multilingual
|
||||
BLOCK "040904E4"
|
||||
{
|
||||
VALUE "FileDescription", "@RC_DESCRIPTION@"
|
||||
VALUE "FileVersion", "@RC_VERSION_STRING@"
|
||||
VALUE "ProductName", "@RC_APPLICATION_NAME@"
|
||||
VALUE "ProductVersion", "@RC_VERSION_STRING@"
|
||||
VALUE "LegalCopyright", "@RC_COPYRIGHT@"
|
||||
}
|
||||
}
|
||||
BLOCK "VarFileInfo"
|
||||
{
|
||||
VALUE "Translation", 0x409, 1252 // 1252 = 0x04E4
|
||||
}
|
||||
}
|
||||
38
libs/qwindowkit/qmsetup/cmake/windows/WinResource2.rc.in
Normal file
38
libs/qwindowkit/qmsetup/cmake/windows/WinResource2.rc.in
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <windows.h>
|
||||
|
||||
@RC_ICONS@
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION @RC_VERSION@
|
||||
PRODUCTVERSION @RC_VERSION@
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE @RC_FILE_TYPE@
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "Comments", "@RC_COMMENTS@"
|
||||
VALUE "CompanyName", "@RC_COMPANY@"
|
||||
VALUE "FileDescription", "@RC_DESCRIPTION@"
|
||||
VALUE "FileVersion", "@RC_VERSION_STRING@"
|
||||
VALUE "InternalName", "@RC_INTERNAL_NAME@"
|
||||
VALUE "LegalCopyright", "@RC_COPYRIGHT@"
|
||||
VALUE "LegalTrademarks", "@RC_TRADEMARK@"
|
||||
VALUE "OriginalFilename", "@RC_ORIGINAL_FILENAME@"
|
||||
VALUE "ProductName", "@RC_APPLICATION_NAME@"
|
||||
VALUE "ProductVersion", "@RC_VERSION_STRING@"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
56
libs/qwindowkit/qmsetup/docs/core-command.md
Normal file
56
libs/qwindowkit/qmsetup/docs/core-command.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Core Command
|
||||
|
||||
`qmcorecmd` is an executable written by C++. It is designed to provide a set of solutions that are fixed in form and that CMake scripts cannot implement due to insufficient features or inefficiencies.
|
||||
|
||||
`qmcorecmd` uses `syscmdline` to parse command line arguments and have multiple subcommands.
|
||||
|
||||
You can refer to the help message of each command to acquire more usage information, since this document provides only a brief introduction.
|
||||
|
||||
## Filesystem Commands
|
||||
|
||||
### Copy
|
||||
|
||||
Copies a file or folder. If the given folder path ends with the separator, copies the contents of the folder.
|
||||
|
||||
+ Read timestamp to determine whether to overwrite an existing object file.
|
||||
+ Support ignoring specific files specified by regular expressions.
|
||||
|
||||
### Rmdir
|
||||
|
||||
Recursively deletes all empty directories.
|
||||
|
||||
Since CMake copies all directories unconditionally when executing the `install(DIRECTORY ...)` command, even if the directory is empty. This command can be used to fix this unreasonable phenomenon.
|
||||
|
||||
### Touch
|
||||
|
||||
Since Microsoft Windows does not provide a native command for handling file timestamps, this command is designed to copy or update a file's time stamp.
|
||||
|
||||
Only some of the functionality of Unix `touch` command is implemented.
|
||||
|
||||
## Buildsystem Commands
|
||||
|
||||
### Configure
|
||||
|
||||
Generates configuration header files.
|
||||
|
||||
It allows users to add macro definitions via the `-D` option, which are directly written into the generated header file. Additionally, users can specify a warning file with the `-w` option, the contents of which are added as comments at the top of the header file.
|
||||
|
||||
A key feature of this command is its ability to compute the SHA-256 hash of the definitions and embed it in the generated file, facilitating the tracking of configuration changes.
|
||||
|
||||
### Incsync
|
||||
|
||||
Reorganizes header files in include directories.
|
||||
|
||||
It supports defining path patterns and corresponding subdirectories through the `-i` option, reorganizing header files by writing relative reference statement or copying directly. The `-e` option can be used to exclude files matching certain patterns.
|
||||
|
||||
While processing header files, `incsync` considers the last modified time to determine whether to copy or update files.
|
||||
|
||||
### Deploy
|
||||
|
||||
Resolves and deploys a binary's shared library dependencies.
|
||||
|
||||
It analyzes dependencies of specified files and copies these dependencies to a designated output directory. Moreover, it handles details like fixing RPATH or dealing with Windows-specific library search paths.
|
||||
|
||||
The `-s` option is provided to ignore standard C/C++ runtime and system libraries, reducing unnecessary dependency copying.
|
||||
|
||||
All shared libraries that are deployed must have different file names.
|
||||
12
libs/qwindowkit/qmsetup/include/qmsetup/qmsetup_global.h
Normal file
12
libs/qwindowkit/qmsetup/include/qmsetup/qmsetup_global.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef QMSETUP_GLOBAL_H
|
||||
#define QMSETUP_GLOBAL_H
|
||||
|
||||
#ifdef _WIN32
|
||||
# define QMSETUP_DECL_EXPORT __declspec(dllexport)
|
||||
# define QMSETUP_DECL_IMPORT __declspec(dllimport)
|
||||
#else
|
||||
# define QMSETUP_DECL_EXPORT __attribute__((visibility("default")))
|
||||
# define QMSETUP_DECL_IMPORT
|
||||
#endif
|
||||
|
||||
#endif // QMSETUP_GLOBAL_H
|
||||
79
libs/qwindowkit/qmsetup/src/CMakeLists.txt
Normal file
79
libs/qwindowkit/qmsetup/src/CMakeLists.txt
Normal file
@@ -0,0 +1,79 @@
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
if(QMSETUP_STATIC_RUNTIME)
|
||||
# To avoid a command execution that can't find the library, we choose to link compiler runtime statically
|
||||
if(MINGW)
|
||||
add_link_options(-static -static-libgcc -static-libstdc++)
|
||||
elseif(MSVC)
|
||||
add_compile_options(/MT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Get syscmdline
|
||||
find_package(syscmdline QUIET)
|
||||
|
||||
if(NOT TARGET syscmdline::syscmdline)
|
||||
set(SYSCMDLINE_INSTALL off)
|
||||
add_subdirectory(syscmdline)
|
||||
endif()
|
||||
|
||||
# Add subdirectories
|
||||
add_subdirectory(corecmd)
|
||||
|
||||
# Add headers
|
||||
set(_corelib_name library)
|
||||
add_library(${_corelib_name} INTERFACE)
|
||||
target_include_directories(${_corelib_name} INTERFACE
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${QMSETUP_INSTALL_NAME}>"
|
||||
)
|
||||
|
||||
install(TARGETS ${_corelib_name}
|
||||
EXPORT ${QMSETUP_INSTALL_NAME}Targets
|
||||
)
|
||||
|
||||
if(TRUE)
|
||||
# Add install target
|
||||
set(_install_dir ${CMAKE_INSTALL_LIBDIR}/cmake/${QMSETUP_INSTALL_NAME})
|
||||
|
||||
# Add version file
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${QMSETUP_INSTALL_NAME}ConfigVersion.cmake"
|
||||
VERSION ${QMSETUP_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
# Add configuration file
|
||||
configure_package_config_file(
|
||||
${CMAKE_CURRENT_LIST_DIR}/${QMSETUP_INSTALL_NAME}Config.cmake.in
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${QMSETUP_INSTALL_NAME}Config.cmake"
|
||||
INSTALL_DESTINATION ${_install_dir}
|
||||
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
||||
)
|
||||
|
||||
# Install cmake files
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${QMSETUP_INSTALL_NAME}Config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${QMSETUP_INSTALL_NAME}ConfigVersion.cmake"
|
||||
DESTINATION ${_install_dir}
|
||||
)
|
||||
|
||||
# Install cmake targets files
|
||||
install(EXPORT ${QMSETUP_INSTALL_NAME}Targets
|
||||
FILE "${QMSETUP_INSTALL_NAME}Targets.cmake"
|
||||
NAMESPACE qmsetup::
|
||||
DESTINATION ${_install_dir}
|
||||
)
|
||||
|
||||
# Install headers
|
||||
install(DIRECTORY ../include/
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
|
||||
# Install cmake modules
|
||||
install(DIRECTORY ../cmake/
|
||||
DESTINATION ${_install_dir}/cmake
|
||||
)
|
||||
endif()
|
||||
89
libs/qwindowkit/qmsetup/src/corecmd/CMakeLists.txt
Normal file
89
libs/qwindowkit/qmsetup/src/corecmd/CMakeLists.txt
Normal file
@@ -0,0 +1,89 @@
|
||||
project(qmcorecmd VERSION ${QMSETUP_VERSION})
|
||||
|
||||
string(TIMESTAMP _year "%Y")
|
||||
set(RC_COPYRIGHT "Copyright 2023-${_year} Stdware Collections")
|
||||
set(RC_DESCRIPTION "QMSetup Core Utility Command")
|
||||
|
||||
set(_src
|
||||
main.cpp
|
||||
sha-256.h
|
||||
sha-256.cpp
|
||||
utils.h
|
||||
utils.cpp
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND _src utils_win.cpp)
|
||||
else()
|
||||
list(APPEND _src utils_unix.cpp)
|
||||
endif()
|
||||
|
||||
add_executable(${PROJECT_NAME} ${_src})
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE syscmdline::syscmdline)
|
||||
|
||||
# Add features
|
||||
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20 cxx_std_17)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
CXX_EXTENSIONS OFF
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
)
|
||||
|
||||
# Compat with gcc 8
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9")
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE stdc++fs)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE shlwapi)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
qm_add_win_rc(${PROJECT_NAME}
|
||||
COPYRIGHT ${RC_COPYRIGHT}
|
||||
DESCRIPTION ${RC_DESCRIPTION}
|
||||
)
|
||||
|
||||
# Make sure Windows UAC does not believe it is an installer
|
||||
qm_add_win_manifest(${PROJECT_NAME}
|
||||
DESCRIPTION ${RC_DESCRIPTION}
|
||||
)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE TOOL_DESC="${RC_DESCRIPTION}, Version ${QMSETUP_VERSION}")
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE TOOL_COPYRIGHT="${RC_COPYRIGHT}, checkout https://github.com/stdware/qmsetup")
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE TOOL_VERSION="${QMSETUP_VERSION}")
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES EXPORT_NAME corecmd)
|
||||
|
||||
# Vcpkg requires the binary to be installed in "tools/${PORT}" directory
|
||||
if(QMSETUP_VCPKG_TOOLS_HINT)
|
||||
set(_tools_dir tools/${QMSETUP_INSTALL_NAME})
|
||||
else()
|
||||
set(_tools_dir ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
EXPORT ${QMSETUP_INSTALL_NAME}Targets
|
||||
RUNTIME DESTINATION "${_tools_dir}" OPTIONAL
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" OPTIONAL
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" OPTIONAL
|
||||
)
|
||||
|
||||
# Check patchelf
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
execute_process(
|
||||
COMMAND patchelf --version
|
||||
OUTPUT_VARIABLE _patchelf_version_output
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
if(NOT "${_patchelf_version_output}" MATCHES "patchelf")
|
||||
message(WARNING "Patchelf not found, the deploy feature won't work.")
|
||||
else()
|
||||
string(REGEX REPLACE "patchelf (.+)" "\\1" _patchelf_version ${_patchelf_version_output})
|
||||
message(STATUS "Found patchelf, version ${_patchelf_version}")
|
||||
endif()
|
||||
endif()
|
||||
1629
libs/qwindowkit/qmsetup/src/corecmd/main.cpp
Normal file
1629
libs/qwindowkit/qmsetup/src/corecmd/main.cpp
Normal file
File diff suppressed because it is too large
Load Diff
210
libs/qwindowkit/qmsetup/src/corecmd/sha-256.cpp
Normal file
210
libs/qwindowkit/qmsetup/src/corecmd/sha-256.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
#include "sha-256.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
static constexpr const auto CHUNK_SIZE = 64;
|
||||
static constexpr const auto TOTAL_LEN_LEN = 8;
|
||||
|
||||
/*
|
||||
* ABOUT bool: this file does not use bool in order to be as pre-C99 compatible as possible.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Comments from pseudo-code at https://en.wikipedia.org/wiki/SHA-2 are reproduced here.
|
||||
* When useful for clarification, portions of the pseudo-code are reproduced here too.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Initialize array of round constants:
|
||||
* (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
|
||||
*/
|
||||
static constexpr const uint32_t k[] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
};
|
||||
|
||||
struct buffer_state {
|
||||
const uint8_t * p;
|
||||
size_t len;
|
||||
size_t total_len;
|
||||
int single_one_delivered; /* bool */
|
||||
int total_len_delivered; /* bool */
|
||||
};
|
||||
|
||||
static inline constexpr uint32_t right_rot(uint32_t value, unsigned int count)
|
||||
{
|
||||
/*
|
||||
* Defined behaviour in standard C for all count where 0 < count < 32,
|
||||
* which is what we need here.
|
||||
*/
|
||||
return value >> count | value << (32 - count);
|
||||
}
|
||||
|
||||
static void init_buf_state(struct buffer_state * state, const void * input, size_t len)
|
||||
{
|
||||
state->p = static_cast<const uint8_t*>(input);
|
||||
state->len = len;
|
||||
state->total_len = len;
|
||||
state->single_one_delivered = 0;
|
||||
state->total_len_delivered = 0;
|
||||
}
|
||||
|
||||
/* Return value: bool */
|
||||
static int calc_chunk(uint8_t chunk[CHUNK_SIZE], struct buffer_state * state)
|
||||
{
|
||||
size_t space_in_chunk;
|
||||
|
||||
if (state->total_len_delivered) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state->len >= CHUNK_SIZE) {
|
||||
std::memcpy(chunk, state->p, CHUNK_SIZE);
|
||||
state->p += CHUNK_SIZE;
|
||||
state->len -= CHUNK_SIZE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::memcpy(chunk, state->p, state->len);
|
||||
chunk += state->len;
|
||||
space_in_chunk = CHUNK_SIZE - state->len;
|
||||
state->p += state->len;
|
||||
state->len = 0;
|
||||
|
||||
/* If we are here, space_in_chunk is one at minimum. */
|
||||
if (!state->single_one_delivered) {
|
||||
*chunk++ = 0x80;
|
||||
space_in_chunk -= 1;
|
||||
state->single_one_delivered = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now:
|
||||
* - either there is enough space left for the total length, and we can conclude,
|
||||
* - or there is too little space left, and we have to pad the rest of this chunk with zeroes.
|
||||
* In the latter case, we will conclude at the next invokation of this function.
|
||||
*/
|
||||
if (space_in_chunk >= TOTAL_LEN_LEN) {
|
||||
const size_t left = space_in_chunk - TOTAL_LEN_LEN;
|
||||
size_t len = state->total_len;
|
||||
int i;
|
||||
std::memset(chunk, 0x00, left);
|
||||
chunk += left;
|
||||
|
||||
/* Storing of len * 8 as a big endian 64-bit without overflow. */
|
||||
chunk[7] = (uint8_t) (len << 3);
|
||||
len >>= 5;
|
||||
for (i = 6; i >= 0; i--) {
|
||||
chunk[i] = (uint8_t) len;
|
||||
len >>= 8;
|
||||
}
|
||||
state->total_len_delivered = 1;
|
||||
} else {
|
||||
std::memset(chunk, 0x00, space_in_chunk);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Limitations:
|
||||
* - Since input is a pointer in RAM, the data to hash should be in RAM, which could be a problem
|
||||
* for large data sizes.
|
||||
* - SHA algorithms theoretically operate on bit strings. However, this implementation has no support
|
||||
* for bit string lengths that are not multiples of eight, and it really operates on arrays of bytes.
|
||||
* In particular, the len parameter is a number of bytes.
|
||||
*/
|
||||
void calc_sha_256(uint8_t hash[32], const void * input, size_t len)
|
||||
{
|
||||
/*
|
||||
* Note 1: All integers (expect indexes) are 32-bit unsigned integers and addition is calculated modulo 2^32.
|
||||
* Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 = i = 63
|
||||
* Note 3: The compression function uses 8 working variables, a through h
|
||||
* Note 4: Big-endian convention is used when expressing the constants in this pseudocode,
|
||||
* and when parsing message block data from bytes to words, for example,
|
||||
* the first word of the input message "abc" after padding is 0x61626380
|
||||
*/
|
||||
|
||||
/*
|
||||
* Initialize hash values:
|
||||
* (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
|
||||
*/
|
||||
uint32_t h[] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };
|
||||
int i, j;
|
||||
|
||||
/* 512-bit chunks is what we will operate on. */
|
||||
uint8_t chunk[64];
|
||||
|
||||
struct buffer_state state;
|
||||
|
||||
init_buf_state(&state, input, len);
|
||||
|
||||
while (calc_chunk(chunk, &state)) {
|
||||
uint32_t ah[8];
|
||||
|
||||
/*
|
||||
* create a 64-entry message schedule array w[0..63] of 32-bit words
|
||||
* (The initial values in w[0..63] don't matter, so many implementations zero them here)
|
||||
* copy chunk into first 16 words w[0..15] of the message schedule array
|
||||
*/
|
||||
uint32_t w[64];
|
||||
const uint8_t *p = chunk;
|
||||
|
||||
std::memset(w, 0x00, sizeof w);
|
||||
for (i = 0; i < 16; i++) {
|
||||
w[i] = (uint32_t) p[0] << 24 | (uint32_t) p[1] << 16 |
|
||||
(uint32_t) p[2] << 8 | (uint32_t) p[3];
|
||||
p += 4;
|
||||
}
|
||||
|
||||
/* Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array: */
|
||||
for (i = 16; i < 64; i++) {
|
||||
const uint32_t s0 = right_rot(w[i - 15], 7) ^ right_rot(w[i - 15], 18) ^ (w[i - 15] >> 3);
|
||||
const uint32_t s1 = right_rot(w[i - 2], 17) ^ right_rot(w[i - 2], 19) ^ (w[i - 2] >> 10);
|
||||
w[i] = w[i - 16] + s0 + w[i - 7] + s1;
|
||||
}
|
||||
|
||||
/* Initialize working variables to current hash value: */
|
||||
for (i = 0; i < 8; i++)
|
||||
ah[i] = h[i];
|
||||
|
||||
/* Compression function main loop: */
|
||||
for (i = 0; i < 64; i++) {
|
||||
const uint32_t s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25);
|
||||
const uint32_t ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]);
|
||||
const uint32_t temp1 = ah[7] + s1 + ch + k[i] + w[i];
|
||||
const uint32_t s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22);
|
||||
const uint32_t maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]);
|
||||
const uint32_t temp2 = s0 + maj;
|
||||
|
||||
ah[7] = ah[6];
|
||||
ah[6] = ah[5];
|
||||
ah[5] = ah[4];
|
||||
ah[4] = ah[3] + temp1;
|
||||
ah[3] = ah[2];
|
||||
ah[2] = ah[1];
|
||||
ah[1] = ah[0];
|
||||
ah[0] = temp1 + temp2;
|
||||
}
|
||||
|
||||
/* Add the compressed chunk to the current hash value: */
|
||||
for (i = 0; i < 8; i++)
|
||||
h[i] += ah[i];
|
||||
}
|
||||
|
||||
/* Produce the final hash value (big-endian): */
|
||||
for (i = 0, j = 0; i < 8; i++)
|
||||
{
|
||||
hash[j++] = (uint8_t) (h[i] >> 24);
|
||||
hash[j++] = (uint8_t) (h[i] >> 16);
|
||||
hash[j++] = (uint8_t) (h[i] >> 8);
|
||||
hash[j++] = (uint8_t) h[i];
|
||||
}
|
||||
}
|
||||
19
libs/qwindowkit/qmsetup/src/corecmd/sha-256.h
Normal file
19
libs/qwindowkit/qmsetup/src/corecmd/sha-256.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef SHA_256_H
|
||||
#define SHA_256_H
|
||||
|
||||
// https://github.com/notepad-plus-plus/notepad-plus-plus/blob/master/PowerEditor/src/MISC/sha2
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void calc_sha_256(uint8_t hash[32], const void *input, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SHA_256_H
|
||||
31
libs/qwindowkit/qmsetup/src/corecmd/utils.cpp
Normal file
31
libs/qwindowkit/qmsetup/src/corecmd/utils.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "utils.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
#ifdef _WIN32
|
||||
# define _TSTR(X) L##X
|
||||
# define tstrcmp wcscmp
|
||||
#else
|
||||
# define _TSTR(X) X
|
||||
# define tstrcmp strcmp
|
||||
#endif
|
||||
|
||||
namespace Utils {
|
||||
|
||||
std::filesystem::path cleanPath(const std::filesystem::path &path) {
|
||||
fs::path result;
|
||||
for (const auto &part : path) {
|
||||
if (part == _TSTR("..")) {
|
||||
if (!result.empty() && result.filename() != _TSTR("..")) {
|
||||
result = result.parent_path();
|
||||
} else {
|
||||
result /= part;
|
||||
}
|
||||
} else if (part != _TSTR(".")) {
|
||||
result /= part;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
158
libs/qwindowkit/qmsetup/src/corecmd/utils.h
Normal file
158
libs/qwindowkit/qmsetup/src/corecmd/utils.h
Normal file
@@ -0,0 +1,158 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <vector>
|
||||
|
||||
namespace Utils {
|
||||
|
||||
// Filesystem Utils
|
||||
struct FileTime {
|
||||
std::chrono::system_clock::time_point accessTime;
|
||||
std::chrono::system_clock::time_point modifyTime;
|
||||
std::chrono::system_clock::time_point statusChangeTime; // Creation time on Windows
|
||||
};
|
||||
|
||||
FileTime fileTime(const std::filesystem::path &path);
|
||||
|
||||
void setFileTime(const std::filesystem::path &path, const FileTime ×);
|
||||
|
||||
inline void syncFileTime(const std::filesystem::path &dest, const std::filesystem::path &src) {
|
||||
setFileTime(dest, fileTime(src));
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> getPathsFromEnv();
|
||||
|
||||
std::filesystem::path cleanPath(const std::filesystem::path &path);
|
||||
|
||||
// String Utils
|
||||
template <class T>
|
||||
std::vector<std::basic_string<T>> split(const std::basic_string<T> &s,
|
||||
const std::basic_string<T> &delimiter) {
|
||||
std::vector<std::basic_string<T>> tokens;
|
||||
typename std::basic_string<T>::size_type start = 0;
|
||||
typename std::basic_string<T>::size_type end = s.find(delimiter);
|
||||
while (end != std::basic_string<T>::npos) {
|
||||
tokens.push_back(s.substr(start, end - start));
|
||||
start = end + delimiter.size();
|
||||
end = s.find(delimiter, start);
|
||||
}
|
||||
tokens.push_back(s.substr(start));
|
||||
return tokens;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::basic_string<T> trim(const std::basic_string<T> &str) {
|
||||
auto start = str.begin();
|
||||
while (start != str.end() && std::isspace(*start)) {
|
||||
start++;
|
||||
}
|
||||
|
||||
auto end = str.end();
|
||||
do {
|
||||
end--;
|
||||
} while (std::distance(start, end) > 0 && std::isspace(*end));
|
||||
|
||||
return {start, end + 1};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void replaceString(std::basic_string<T> &s, const std::basic_string<T> &pattern,
|
||||
const std::basic_string<T> &text) {
|
||||
size_t idx = 0;
|
||||
while ((idx = s.find(pattern, idx)) != std::basic_string<T>::npos) {
|
||||
s.replace(idx, pattern.size(), text);
|
||||
idx += text.size();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
std::basic_string<T> join(const std::vector<std::basic_string<T>> &v,
|
||||
const std::basic_string<T> &delimiter) {
|
||||
if (v.empty())
|
||||
return {};
|
||||
|
||||
std::basic_string<T> res;
|
||||
for (int i = 0; i < v.size() - 1; ++i) {
|
||||
res.append(v[i]);
|
||||
res.append(delimiter);
|
||||
}
|
||||
res.append(v.back());
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::basic_string<T> toLower(std::basic_string<T> s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
|
||||
return s;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::basic_string<T> toUpper(std::basic_string<T> s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
|
||||
return s;
|
||||
}
|
||||
|
||||
inline bool starts_with(const std::string_view &s, const std::string_view &prefix) {
|
||||
#if __cplusplus >= 202002L
|
||||
return s.starts_with(prefix);
|
||||
#else
|
||||
return s.size() >= prefix.size() && s.substr(0, prefix.size()) == prefix;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool starts_with(const std::wstring_view &s, const std::wstring_view &prefix) {
|
||||
#if __cplusplus >= 202002L
|
||||
return s.starts_with(prefix);
|
||||
#else
|
||||
return s.size() >= prefix.size() && s.substr(0, prefix.size()) == prefix;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class Container, class T>
|
||||
inline bool contains(const Container &container, const T &key) {
|
||||
#if __cplusplus >= 202002L
|
||||
return container.contains(key);
|
||||
#else
|
||||
return container.count(key) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// OS Utils
|
||||
#ifdef _WIN32
|
||||
std::wstring executeCommand(const std::wstring &command, const std::vector<std::wstring> &args);
|
||||
|
||||
std::vector<std::wstring>
|
||||
resolveWinBinaryDependencies(const std::filesystem::path &path,
|
||||
const std::vector<std::filesystem::path> &searchingPaths,
|
||||
std::vector<std::string> *unparsed);
|
||||
#else
|
||||
std::string executeCommand(const std::string &command, const std::vector<std::string> &args);
|
||||
|
||||
void setFileRPaths(const std::string &file, const std::vector<std::string> &paths);
|
||||
|
||||
std::vector<std::string>
|
||||
resolveUnixBinaryDependencies(const std::filesystem::path &path,
|
||||
const std::vector<std::filesystem::path> &searchingPaths,
|
||||
std::vector<std::string> *unparsed = nullptr);
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
std::vector<std::string> getMacAbsoluteDependencies(const std::string &file);
|
||||
|
||||
void replaceMacFileDependencies(
|
||||
const std::string &file, const std::vector<std::pair<std::string, std::string>> &depPairs);
|
||||
#elif defined(__linux__)
|
||||
std::string getInterpreter(const std::string &file);
|
||||
|
||||
bool setFileInterpreter(const std::string &file, const std::string &interpreter);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#endif // UTILS_H
|
||||
500
libs/qwindowkit/qmsetup/src/corecmd/utils_unix.cpp
Normal file
500
libs/qwindowkit/qmsetup/src/corecmd/utils_unix.cpp
Normal file
@@ -0,0 +1,500 @@
|
||||
#include "utils.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace Utils {
|
||||
|
||||
std::string executeCommand(const std::string &command, const std::vector<std::string> &args) {
|
||||
// printf("Executing command: %s", command.data());
|
||||
// for (const auto &arg : args) {
|
||||
// printf(" %s", arg.data());
|
||||
// }
|
||||
// printf("\n");
|
||||
|
||||
static const auto &dupStr = [](char *&dest, const char *src, size_t size) {
|
||||
dest = new char[size + 1];
|
||||
memcpy(dest, src, size);
|
||||
dest[size] = '\0';
|
||||
};
|
||||
|
||||
enum PipeNum {
|
||||
PN_Read,
|
||||
PN_Write,
|
||||
};
|
||||
|
||||
// Create pipe
|
||||
int pipefd[2];
|
||||
if (pipe(pipefd) == -1) {
|
||||
throw std::runtime_error("failed to call \"pipe\"");
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
// Close pipes right away
|
||||
close(pipefd[PN_Read]);
|
||||
close(pipefd[PN_Write]);
|
||||
throw std::runtime_error("failed to call \"fork\"");
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
// --------
|
||||
// Child process
|
||||
// --------
|
||||
|
||||
// Close read pipe
|
||||
close(pipefd[PN_Read]);
|
||||
|
||||
// Redirect "stdout" and "stderr" to write pipe
|
||||
dup2(pipefd[PN_Write], STDOUT_FILENO);
|
||||
dup2(pipefd[PN_Write], STDERR_FILENO);
|
||||
|
||||
// Prepare arguments
|
||||
auto argv = new char *[args.size() + 2]; // +2 for command and nullptr
|
||||
dupStr(argv[0], command.data(), command.size());
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
dupStr(argv[i + 1], args[i].data(), args[i].size());
|
||||
}
|
||||
argv[args.size() + 1] = nullptr;
|
||||
|
||||
// Call "exec"
|
||||
execvp(argv[0], argv);
|
||||
|
||||
// If the control flow reaches here, there must be a mistake
|
||||
|
||||
// Clean allocated memory
|
||||
for (size_t i = 0; i < args.size() + 2; ++i) {
|
||||
delete[] argv[i];
|
||||
}
|
||||
delete[] argv;
|
||||
|
||||
// Fail
|
||||
printf("exec \"%s\" failed: %s\n", command.data(), strerror(errno));
|
||||
|
||||
// No need to close write pipe, let the System handle everything
|
||||
// Simply exit
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// --------
|
||||
// Parent process
|
||||
// --------
|
||||
|
||||
// Close write pipe
|
||||
close(pipefd[PN_Write]);
|
||||
|
||||
// Read child process output
|
||||
char buffer[256];
|
||||
std::string output;
|
||||
ssize_t bytesRead;
|
||||
while ((bytesRead = read(pipefd[0], buffer, sizeof(buffer) - 1)) > 0) {
|
||||
buffer[bytesRead] = '\0';
|
||||
output += buffer;
|
||||
}
|
||||
|
||||
// Close read pipe
|
||||
close(pipefd[PN_Read]);
|
||||
|
||||
// Wait for child process to terminate
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
|
||||
// Check exit status
|
||||
if (WIFEXITED(status)) {
|
||||
auto exitCode = WEXITSTATUS(status);
|
||||
if (exitCode == 0)
|
||||
return output;
|
||||
|
||||
// Throw error
|
||||
throw std::runtime_error(trim(output));
|
||||
}
|
||||
|
||||
if (WIFSIGNALED(status)) {
|
||||
auto sig = WTERMSIG(status);
|
||||
throw std::runtime_error("command \"" + command + "\" was terminated by signal " +
|
||||
std::to_string(sig));
|
||||
}
|
||||
|
||||
throw std::runtime_error("command \"" + command + "\" terminated abnormally with status " +
|
||||
std::to_string(status));
|
||||
}
|
||||
|
||||
FileTime fileTime(const fs::path &path) {
|
||||
struct stat sb;
|
||||
if (stat(path.c_str(), &sb) == -1) {
|
||||
throw std::runtime_error("failed to get file time: \"" + path.string() + "\"");
|
||||
}
|
||||
|
||||
FileTime times;
|
||||
times.accessTime = std::chrono::system_clock::from_time_t(sb.st_atime);
|
||||
times.modifyTime = std::chrono::system_clock::from_time_t(sb.st_mtime);
|
||||
times.statusChangeTime = std::chrono::system_clock::from_time_t(sb.st_ctime);
|
||||
return times;
|
||||
}
|
||||
|
||||
void setFileTime(const fs::path &path, const FileTime ×) {
|
||||
struct utimbuf new_times;
|
||||
new_times.actime = std::chrono::system_clock::to_time_t(times.accessTime);
|
||||
new_times.modtime = std::chrono::system_clock::to_time_t(times.modifyTime);
|
||||
if (utime(path.c_str(), &new_times) != 0) {
|
||||
throw std::runtime_error("failed to set file time: \"" + path.string() + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<fs::path> getPathsFromEnv() {
|
||||
const char *pathEnv = std::getenv("PATH");
|
||||
if (pathEnv == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string pathStr = pathEnv;
|
||||
std::stringstream ss(pathStr);
|
||||
std::string item;
|
||||
|
||||
std::vector<fs::path> paths;
|
||||
while (std::getline(ss, item, ':')) {
|
||||
if (!item.empty()) {
|
||||
paths.push_back(fs::path(item));
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Mac
|
||||
// Use `otool` and `install_name_tool`
|
||||
|
||||
static std::vector<std::string> readMacBinaryRPaths(const std::string &path) {
|
||||
std::vector<std::string> rpaths;
|
||||
std::string output;
|
||||
|
||||
try {
|
||||
output = executeCommand("otool", {"-l", path});
|
||||
} catch (const std::exception &e) {
|
||||
throw std::runtime_error("Failed to get RPATHs: " + std::string(e.what()));
|
||||
}
|
||||
|
||||
static const std::regex rpathRegex(R"(\s*path\s+(.*)\s+\(offset.*)");
|
||||
std::istringstream iss(output);
|
||||
std::string line;
|
||||
std::smatch match;
|
||||
|
||||
while (std::getline(iss, line)) {
|
||||
if (line.find("cmd LC_RPATH") != std::string::npos) {
|
||||
// skip 2 lines
|
||||
std::getline(iss, line);
|
||||
std::getline(iss, line);
|
||||
if (std::regex_match(line, match, rpathRegex) && match.size() >= 2) {
|
||||
rpaths.emplace_back(match[1].str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rpaths;
|
||||
}
|
||||
|
||||
static std::vector<std::string> readMacBinaryDependencies(const std::string &path) {
|
||||
std::vector<std::string> dependencies;
|
||||
std::string output;
|
||||
|
||||
// Get dependencies
|
||||
try {
|
||||
output = executeCommand("otool", {"-L", path});
|
||||
} catch (const std::exception &e) {
|
||||
throw std::runtime_error("Failed to get dependencies: " + std::string(e.what()));
|
||||
}
|
||||
|
||||
static const std::regex depRegex(
|
||||
R"(^\t(.+) \(compatibility version (\d+\.\d+\.\d+), current version (\d+\.\d+\.\d+)(, weak)?\)$)");
|
||||
std::istringstream iss(output);
|
||||
std::string line;
|
||||
std::smatch match;
|
||||
|
||||
// skip first line
|
||||
std::getline(iss, line);
|
||||
|
||||
const std::string &loaderPath = path;
|
||||
while (std::getline(iss, line)) {
|
||||
if (std::regex_search(line, match, depRegex) && match.size() >= 2) {
|
||||
std::string dep = match[1].str();
|
||||
dependencies.emplace_back(dep);
|
||||
}
|
||||
}
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
resolveUnixBinaryDependencies(const std::filesystem::path &path,
|
||||
const std::vector<std::filesystem::path> &searchingPaths,
|
||||
std::vector<std::string> *unparsed) {
|
||||
auto rpaths = readMacBinaryRPaths(path);
|
||||
for (const auto &item : searchingPaths) {
|
||||
rpaths.push_back(item);
|
||||
}
|
||||
|
||||
auto dependencies = readMacBinaryDependencies(path);
|
||||
const std::string &loaderPath = fs::canonical(path).parent_path();
|
||||
|
||||
std::vector<std::string> res;
|
||||
for (auto dep : std::as_const(dependencies)) {
|
||||
// Replace @executable_path and @loader_path
|
||||
replaceString(dep, std::string("@executable_path"), loaderPath);
|
||||
replaceString(dep, std::string("@loader_path"), loaderPath);
|
||||
|
||||
// Find dependency
|
||||
std::string target = dep;
|
||||
if (dep.find("@rpath") != std::string::npos) {
|
||||
for (auto rpath : rpaths) {
|
||||
// Replace again
|
||||
replaceString(rpath, std::string("@executable_path"), loaderPath);
|
||||
replaceString(rpath, std::string("@loader_path"), loaderPath);
|
||||
|
||||
std::string fullPath = dep;
|
||||
replaceString(fullPath, std::string("@rpath"), rpath);
|
||||
if (fs::exists(fullPath)) {
|
||||
target = fullPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target = cleanPath(target);
|
||||
if (fs::exists(target)) {
|
||||
if (fs::canonical(target).filename() == fs::canonical(path).filename())
|
||||
continue;
|
||||
res.push_back(target);
|
||||
} else if (unparsed) {
|
||||
unparsed->push_back(target);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void setFileRPaths(const std::string &file, const std::vector<std::string> &paths) {
|
||||
// Remove rpaths
|
||||
do {
|
||||
auto rpaths = readMacBinaryRPaths(file);
|
||||
if (rpaths.empty())
|
||||
break;
|
||||
std::vector<std::string> args;
|
||||
args.reserve(rpaths.size() * 2 + 1);
|
||||
for (const auto &rpath : std::as_const(rpaths)) {
|
||||
args.push_back("-delete_rpath");
|
||||
args.push_back(rpath);
|
||||
}
|
||||
args.push_back(file);
|
||||
|
||||
try {
|
||||
std::ignore = executeCommand("install_name_tool", args);
|
||||
} catch (const std::exception &e) {
|
||||
throw std::runtime_error("Failed to remove rpaths: " + std::string(e.what()));
|
||||
}
|
||||
} while (false);
|
||||
|
||||
// Add rpaths
|
||||
if (!paths.empty()) {
|
||||
std::set<std::string> visited;
|
||||
std::vector<std::string> args;
|
||||
args.reserve(paths.size() * 2 + 1);
|
||||
for (const auto &rpath : std::as_const(paths)) {
|
||||
if (Utils::contains(visited, rpath))
|
||||
continue;
|
||||
|
||||
visited.insert(rpath);
|
||||
args.push_back("-add_rpath");
|
||||
args.push_back(rpath);
|
||||
}
|
||||
args.push_back(file);
|
||||
|
||||
try {
|
||||
std::ignore = executeCommand("install_name_tool", args);
|
||||
} catch (const std::exception &e) {
|
||||
throw std::runtime_error("Failed to add rpaths: " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
// Remove code signature if it exists
|
||||
try {
|
||||
std::ignore = executeCommand("codesign", {"--remove-signature", file});
|
||||
std::ignore = executeCommand("codesign", {"-s", "-", file});
|
||||
} catch (const std::exception &e) {
|
||||
throw std::runtime_error("Failed to resign: " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> getMacAbsoluteDependencies(const std::string &file) {
|
||||
auto deps = readMacBinaryDependencies(file);
|
||||
std::vector<std::string> res;
|
||||
for (const auto &dep : std::as_const(deps)) {
|
||||
if (fs::path(dep).is_absolute()) {
|
||||
res.push_back(dep);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void replaceMacFileDependencies(
|
||||
const std::string &file, const std::vector<std::pair<std::string, std::string>> &depPairs) {
|
||||
std::string output;
|
||||
std::vector<std::string> args;
|
||||
args.reserve(depPairs.size() * 3 + 1);
|
||||
|
||||
std::string id;
|
||||
for (const auto &pair : depPairs) {
|
||||
if (fs::exists(pair.first) &&
|
||||
fs::canonical(pair.first).filename() == fs::canonical(file).filename()) {
|
||||
id = pair.second;
|
||||
continue;
|
||||
}
|
||||
args.push_back("-change");
|
||||
args.push_back(pair.first);
|
||||
args.push_back(pair.second);
|
||||
}
|
||||
if (!id.empty()) {
|
||||
args.push_back("-id");
|
||||
args.push_back(id);
|
||||
}
|
||||
args.push_back(file);
|
||||
|
||||
try {
|
||||
output = executeCommand("install_name_tool", args);
|
||||
} catch (const std::exception &e) {
|
||||
throw std::runtime_error("Failed to replace dependency: " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
// Linux
|
||||
// Use `ldd` and `patchelf`
|
||||
|
||||
static std::vector<std::string>
|
||||
readLddOutput(const std::string &fileName,
|
||||
const std::vector<std::filesystem::path> &searchingPaths,
|
||||
std::vector<std::string> *unparsed) {
|
||||
std::string output;
|
||||
|
||||
try {
|
||||
output = executeCommand("ldd", {fileName});
|
||||
} catch (const std::exception &e) {
|
||||
throw std::runtime_error("Failed to get dependencies: " + std::string(e.what()));
|
||||
}
|
||||
|
||||
std::istringstream iss(output);
|
||||
std::string line;
|
||||
|
||||
static const std::regex regexp("^\\s*.+ => (.+) \\(.*");
|
||||
static const std::regex regexp2("^\\s*(.+) => not found");
|
||||
|
||||
std::vector<std::string> dependencies;
|
||||
while (std::getline(iss, line)) {
|
||||
std::smatch match;
|
||||
if (std::regex_match(line, match, regexp) && match.size() >= 2) {
|
||||
dependencies.push_back(cleanPath(match[1].str()));
|
||||
} else if (std::regex_match(line, match, regexp2) && match.size() >= 2) {
|
||||
// Search in search paths
|
||||
fs::path target;
|
||||
for (const auto &item : searchingPaths) {
|
||||
auto fullPath = item / match[1].str();
|
||||
if (fs::exists(fullPath)) {
|
||||
target = fullPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!target.empty()) {
|
||||
dependencies.push_back(target);
|
||||
} else {
|
||||
unparsed->push_back(cleanPath(match[1].str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
resolveUnixBinaryDependencies(const std::filesystem::path &path,
|
||||
const std::vector<std::filesystem::path> &searchingPaths,
|
||||
std::vector<std::string> *unparsed) {
|
||||
return readLddOutput(path, searchingPaths, unparsed);
|
||||
}
|
||||
|
||||
void setFileRPaths(const std::string &file, const std::vector<std::string> &paths) {
|
||||
if (paths.empty()) {
|
||||
try {
|
||||
std::ignore = executeCommand("patchelf", {
|
||||
"--remove-rpath",
|
||||
file,
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
throw std::runtime_error("Failed to remove rpaths: " + std::string(e.what()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
std::ignore = executeCommand("patchelf", {
|
||||
"--set-rpath",
|
||||
join(paths, std::string(":")),
|
||||
file,
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
throw std::runtime_error("Failed to replace rpaths: " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __linux__
|
||||
static inline bool errorIsInterpNotFound(const std::string &what) {
|
||||
return what.find("cannot find section '.interp'") != std::string::npos;
|
||||
};
|
||||
|
||||
std::string getInterpreter(const std::string &file) {
|
||||
std::string output;
|
||||
try {
|
||||
output = executeCommand("patchelf", {
|
||||
"--print-interpreter",
|
||||
file,
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
if (errorIsInterpNotFound(e.what()))
|
||||
return {};
|
||||
throw std::runtime_error("Failed to get interpreter: " + std::string(e.what()));
|
||||
}
|
||||
|
||||
output = trim(output);
|
||||
replaceString(output, std::string("$ORIGIN"), fs::canonical(file).parent_path().string());
|
||||
return output;
|
||||
}
|
||||
|
||||
bool setFileInterpreter(const std::string &file, const std::string &interpreter) {
|
||||
try {
|
||||
std::ignore = executeCommand("patchelf", {
|
||||
"--set-interpreter",
|
||||
interpreter,
|
||||
file,
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
if (errorIsInterpNotFound(e.what()))
|
||||
return false;
|
||||
throw std::runtime_error("Failed to set interpreter: " + std::string(e.what()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
500
libs/qwindowkit/qmsetup/src/corecmd/utils_win.cpp
Normal file
500
libs/qwindowkit/qmsetup/src/corecmd/utils_win.cpp
Normal file
@@ -0,0 +1,500 @@
|
||||
#include "utils.h"
|
||||
|
||||
#include <shlwapi.h>
|
||||
|
||||
#include <delayimp.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include <syscmdline/system.h>
|
||||
|
||||
namespace SCL = SysCmdLine;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace Utils {
|
||||
|
||||
static constexpr const DWORD g_EnglishLangId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
|
||||
|
||||
static std::wstring winErrorMessage(uint32_t error, bool nativeLanguage = true) {
|
||||
std::wstring rc;
|
||||
wchar_t *lpMsgBuf;
|
||||
|
||||
DWORD len = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, error, nativeLanguage ? 0 : g_EnglishLangId,
|
||||
reinterpret_cast<LPWSTR>(&lpMsgBuf), 0, NULL);
|
||||
|
||||
if (len) {
|
||||
// Remove tail line breaks
|
||||
if (lpMsgBuf[len - 1] == L'\n') {
|
||||
lpMsgBuf[len - 1] = L'\0';
|
||||
if (len > 2 && lpMsgBuf[len - 2] == L'\r') {
|
||||
lpMsgBuf[len - 2] = L'\0';
|
||||
}
|
||||
}
|
||||
rc = std::wstring(lpMsgBuf, int(len));
|
||||
::LocalFree(lpMsgBuf);
|
||||
} else {
|
||||
rc += L"unknown error";
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static std::wstring quoteAndEscapeArg(const std::wstring &arg) {
|
||||
if (arg.find_first_of(L" \t\"") == std::wstring::npos) {
|
||||
// No need to quote or escape
|
||||
return arg;
|
||||
}
|
||||
|
||||
std::wstring escapedArg = L"\"";
|
||||
for (auto ch : arg) {
|
||||
if (ch == L'"') {
|
||||
// Escape double quotes
|
||||
escapedArg += L"\"\"";
|
||||
} else {
|
||||
escapedArg += ch;
|
||||
}
|
||||
}
|
||||
escapedArg += L"\"";
|
||||
return escapedArg;
|
||||
}
|
||||
|
||||
std::wstring executeCommand(const std::wstring &command,
|
||||
const std::vector<std::wstring> &args) {
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
HANDLE hReadPipe, hWritePipe;
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
DWORD read;
|
||||
wchar_t buffer[4096]; // For captured output
|
||||
|
||||
// Initialize security attributes to allow pipe handles to be inherited.
|
||||
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
sa.bInheritHandle = TRUE;
|
||||
sa.lpSecurityDescriptor = NULL;
|
||||
|
||||
// Create a pipe for the child process's STDOUT.
|
||||
if (!::CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) {
|
||||
throw std::runtime_error("failed to call \"CreatePipe\": " +
|
||||
SCL::wideToUtf8(winErrorMessage(::GetLastError())));
|
||||
}
|
||||
|
||||
// Ensure the read handle to the pipe for STDOUT is not inherited.
|
||||
if (!::SetHandleInformation(hReadPipe, HANDLE_FLAG_INHERIT, 0)) {
|
||||
::CloseHandle(hWritePipe);
|
||||
::CloseHandle(hReadPipe);
|
||||
throw std::runtime_error("failed to call \"SetHandleInformation\": " +
|
||||
SCL::wideToUtf8(winErrorMessage(::GetLastError())));
|
||||
}
|
||||
|
||||
// Set up members of the STARTUPINFO structure.
|
||||
ZeroMemory(&si, sizeof(STARTUPINFO));
|
||||
si.cb = sizeof(STARTUPINFO);
|
||||
si.hStdError = hWritePipe;
|
||||
si.hStdOutput = hWritePipe;
|
||||
si.dwFlags |= STARTF_USESTDHANDLES;
|
||||
|
||||
// Construct command line
|
||||
std::wstringstream cmdLineStream;
|
||||
cmdLineStream << quoteAndEscapeArg(command) << L" ";
|
||||
for (const auto &arg : args) {
|
||||
cmdLineStream << quoteAndEscapeArg(arg) << L" ";
|
||||
}
|
||||
std::wstring cmdLine = cmdLineStream.str();
|
||||
|
||||
// Start the child process.
|
||||
if (!::CreateProcessW(NULL, cmdLine.data(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
|
||||
::CloseHandle(hWritePipe);
|
||||
::CloseHandle(hReadPipe);
|
||||
|
||||
throw std::runtime_error("failed to call \"CreateProcess\": " +
|
||||
SCL::wideToUtf8(winErrorMessage(::GetLastError())));
|
||||
}
|
||||
|
||||
// Close the write end of the pipe before reading from the read end of the pipe.
|
||||
::CloseHandle(hWritePipe);
|
||||
|
||||
// Read output from the child process's pipe for STDOUT
|
||||
// and write to the parent process's STDOUT.
|
||||
std::wstring output;
|
||||
while (true) {
|
||||
if (!::ReadFile(hReadPipe, buffer, sizeof(buffer) - sizeof(wchar_t), &read, NULL) ||
|
||||
read == 0)
|
||||
break;
|
||||
|
||||
buffer[read / sizeof(wchar_t)] = L'\0';
|
||||
output += buffer;
|
||||
}
|
||||
|
||||
// Wait until child process exits.
|
||||
::WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
|
||||
DWORD exitCode;
|
||||
::GetExitCodeProcess(pi.hProcess, &exitCode);
|
||||
|
||||
// Close process and thread handles and the read end of the pipe.
|
||||
::CloseHandle(pi.hProcess);
|
||||
::CloseHandle(pi.hThread);
|
||||
::CloseHandle(hReadPipe);
|
||||
|
||||
if (exitCode == 0)
|
||||
return output;
|
||||
|
||||
throw std::runtime_error(SCL::wideToUtf8(output));
|
||||
}
|
||||
|
||||
// Helper functions to convert between FILETIME and std::chrono::system_clock::time_point
|
||||
static std::chrono::system_clock::time_point filetime_to_timepoint(const FILETIME &ft) {
|
||||
// Windows file time starts from January 1, 1601
|
||||
// std::chrono::system_clock starts from January 1, 1970
|
||||
static constexpr const long long WIN_EPOCH =
|
||||
116444736000000000LL; // in hundreds of nanoseconds
|
||||
long long duration = (static_cast<long long>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
|
||||
duration -= WIN_EPOCH; // convert to Unix epoch
|
||||
return std::chrono::system_clock::from_time_t(duration / 10000000LL);
|
||||
}
|
||||
|
||||
static FILETIME timepoint_to_filetime(const std::chrono::system_clock::time_point &tp) {
|
||||
FILETIME ft;
|
||||
static constexpr const long long WIN_EPOCH =
|
||||
116444736000000000LL; // in hundreds of nanoseconds
|
||||
long long duration =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(tp.time_since_epoch()).count();
|
||||
duration = duration * 10 + WIN_EPOCH;
|
||||
ft.dwLowDateTime = static_cast<DWORD>(duration & 0xFFFFFFFF);
|
||||
ft.dwHighDateTime = static_cast<DWORD>((duration >> 32) & 0xFFFFFFFF);
|
||||
return ft;
|
||||
}
|
||||
|
||||
FileTime fileTime(const fs::path &path) {
|
||||
HANDLE hFile = ::CreateFileW(path.wstring().data(), GENERIC_READ, FILE_SHARE_READ, nullptr,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
throw std::runtime_error("invalid path: \"" + SCL::wideToUtf8(path.wstring()) + "\"");
|
||||
}
|
||||
|
||||
FILETIME creationTime, lastAccessTime, lastWriteTime;
|
||||
if (!::GetFileTime(hFile, &creationTime, &lastAccessTime, &lastWriteTime)) {
|
||||
::CloseHandle(hFile);
|
||||
throw std::runtime_error("failed to get file time: \"" +
|
||||
SCL::wideToUtf8(path.wstring()) + "\"");
|
||||
}
|
||||
::CloseHandle(hFile);
|
||||
|
||||
FileTime times;
|
||||
// ... (convert FILETIMEs to std::chrono::system_clock::time_point and store in times)
|
||||
times.accessTime = filetime_to_timepoint(lastAccessTime);
|
||||
times.modifyTime = filetime_to_timepoint(lastWriteTime);
|
||||
times.statusChangeTime = filetime_to_timepoint(creationTime);
|
||||
|
||||
return times;
|
||||
}
|
||||
|
||||
void setFileTime(const fs::path &path, const FileTime ×) {
|
||||
HANDLE hFile = ::CreateFileW(path.wstring().data(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE,
|
||||
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
throw std::runtime_error("invalid path: \"" + SCL::wideToUtf8(path.wstring()) + "\"");
|
||||
}
|
||||
|
||||
FILETIME creationTime, lastAccessTime, lastWriteTime;
|
||||
lastAccessTime = timepoint_to_filetime(times.accessTime);
|
||||
lastWriteTime = timepoint_to_filetime(times.modifyTime);
|
||||
creationTime = timepoint_to_filetime(times.statusChangeTime);
|
||||
|
||||
if (!::SetFileTime(hFile, &creationTime, &lastAccessTime, &lastWriteTime)) {
|
||||
::CloseHandle(hFile);
|
||||
throw std::runtime_error("failed to set file time: \"" +
|
||||
SCL::wideToUtf8(path.wstring()) + "\"");
|
||||
}
|
||||
::CloseHandle(hFile);
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> getPathsFromEnv() {
|
||||
DWORD length = ::GetEnvironmentVariableW(L"PATH", nullptr, 0);
|
||||
if (length == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto buf = new wchar_t[length];
|
||||
::GetEnvironmentVariableW(L"PATH", buf, length);
|
||||
|
||||
std::wstring pathStr = buf;
|
||||
delete[] buf;
|
||||
std::wstringstream wss(pathStr);
|
||||
std::wstring item;
|
||||
|
||||
std::vector<fs::path> paths;
|
||||
while (std::getline(wss, item, L';')) {
|
||||
if (!item.empty()) {
|
||||
paths.push_back(item);
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
// Modified from windeployqt 5.15.2(Copyright Qt company)
|
||||
// ================================================================================
|
||||
namespace WindowsDeployQt {
|
||||
|
||||
static inline std::string stringFromRvaPtr(const void *rvaPtr) {
|
||||
return static_cast<const char *>(rvaPtr);
|
||||
}
|
||||
|
||||
// Helper for reading out PE executable files: Find a section header for an RVA
|
||||
// (IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32).
|
||||
template <class ImageNtHeader>
|
||||
static const IMAGE_SECTION_HEADER *findSectionHeader(DWORD rva,
|
||||
const ImageNtHeader *nTHeader) {
|
||||
const IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION(nTHeader);
|
||||
const IMAGE_SECTION_HEADER *sectionEnd =
|
||||
section + nTHeader->FileHeader.NumberOfSections;
|
||||
for (; section < sectionEnd; ++section)
|
||||
if (rva >= section->VirtualAddress &&
|
||||
rva < (section->VirtualAddress + section->Misc.VirtualSize))
|
||||
return section;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Helper for reading out PE executable files: convert RVA to pointer (IMAGE_NT_HEADERS64,
|
||||
// IMAGE_NT_HEADERS32).
|
||||
template <class ImageNtHeader>
|
||||
inline const void *rvaToPtr(DWORD rva, const ImageNtHeader *nTHeader,
|
||||
const void *imageBase) {
|
||||
const IMAGE_SECTION_HEADER *sectionHdr = findSectionHeader(rva, nTHeader);
|
||||
if (!sectionHdr)
|
||||
return 0;
|
||||
const DWORD delta = sectionHdr->VirtualAddress - sectionHdr->PointerToRawData;
|
||||
return static_cast<const char *>(imageBase) + rva - delta;
|
||||
}
|
||||
|
||||
// Helper for reading out PE executable files: return word size of a IMAGE_NT_HEADERS64,
|
||||
// IMAGE_NT_HEADERS32
|
||||
template <class ImageNtHeader>
|
||||
static unsigned ntHeaderWordSize(const ImageNtHeader *header) {
|
||||
// defines IMAGE_NT_OPTIONAL_HDR32_MAGIC, IMAGE_NT_OPTIONAL_HDR64_MAGIC
|
||||
enum { imageNtOptionlHeader32Magic = 0x10b, imageNtOptionlHeader64Magic = 0x20b };
|
||||
if (header->OptionalHeader.Magic == imageNtOptionlHeader32Magic)
|
||||
return 32;
|
||||
if (header->OptionalHeader.Magic == imageNtOptionlHeader64Magic)
|
||||
return 64;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Helper for reading out PE executable files: Retrieve the NT image header of an
|
||||
// executable via the legacy DOS header.
|
||||
static IMAGE_NT_HEADERS *getNtHeader(void *fileMemory, std::wstring *errorMessage) {
|
||||
IMAGE_DOS_HEADER *dosHeader = static_cast<PIMAGE_DOS_HEADER>(fileMemory);
|
||||
// Check DOS header consistency
|
||||
if (IsBadReadPtr(dosHeader, sizeof(IMAGE_DOS_HEADER)) ||
|
||||
dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
|
||||
*errorMessage = L"DOS header check failed.";
|
||||
return 0;
|
||||
}
|
||||
// Retrieve NT header
|
||||
char *ntHeaderC = static_cast<char *>(fileMemory) + dosHeader->e_lfanew;
|
||||
IMAGE_NT_HEADERS *ntHeaders = reinterpret_cast<IMAGE_NT_HEADERS *>(ntHeaderC);
|
||||
// check NT header consistency
|
||||
if (IsBadReadPtr(ntHeaders, sizeof(ntHeaders->Signature)) ||
|
||||
ntHeaders->Signature != IMAGE_NT_SIGNATURE ||
|
||||
IsBadReadPtr(&ntHeaders->FileHeader, sizeof(IMAGE_FILE_HEADER))) {
|
||||
*errorMessage = L"NT header check failed.";
|
||||
return 0;
|
||||
}
|
||||
// Check magic
|
||||
if (!ntHeaderWordSize(ntHeaders)) {
|
||||
std::wostringstream ss;
|
||||
ss << "NT header check failed; magic " << ntHeaders->OptionalHeader.Magic
|
||||
<< " is invalid.";
|
||||
*errorMessage = ss.str();
|
||||
return 0;
|
||||
}
|
||||
// Check section headers
|
||||
IMAGE_SECTION_HEADER *sectionHeaders = IMAGE_FIRST_SECTION(ntHeaders);
|
||||
if (IsBadReadPtr(sectionHeaders, ntHeaders->FileHeader.NumberOfSections *
|
||||
sizeof(IMAGE_SECTION_HEADER))) {
|
||||
*errorMessage = L"NT header section header check failed.";
|
||||
return 0;
|
||||
}
|
||||
return ntHeaders;
|
||||
}
|
||||
|
||||
// Helper for reading out PE executable files: Read out import sections from
|
||||
// IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32.
|
||||
template <class ImageNtHeader>
|
||||
static std::vector<std::string> readImportSections(const ImageNtHeader *ntHeaders,
|
||||
const void *base,
|
||||
std::wstring *errorMessage) {
|
||||
// Get import directory entry RVA and read out
|
||||
const DWORD importsStartRVA =
|
||||
ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
|
||||
.VirtualAddress;
|
||||
if (!importsStartRVA) {
|
||||
*errorMessage = L"Failed to find IMAGE_DIRECTORY_ENTRY_IMPORT entry.";
|
||||
return {};
|
||||
}
|
||||
const IMAGE_IMPORT_DESCRIPTOR *importDesc =
|
||||
static_cast<const IMAGE_IMPORT_DESCRIPTOR *>(
|
||||
rvaToPtr(importsStartRVA, ntHeaders, base));
|
||||
if (!importDesc) {
|
||||
*errorMessage = L"Failed to find IMAGE_IMPORT_DESCRIPTOR entry.";
|
||||
return {};
|
||||
}
|
||||
std::vector<std::string> result;
|
||||
for (; importDesc->Name; ++importDesc)
|
||||
result.push_back(stringFromRvaPtr(rvaToPtr(importDesc->Name, ntHeaders, base)));
|
||||
|
||||
// Read delay-loaded DLLs, see http://msdn.microsoft.com/en-us/magazine/cc301808.aspx .
|
||||
// Check on grAttr bit 1 whether this is the format using RVA's > VS 6
|
||||
if (const DWORD delayedImportsStartRVA =
|
||||
ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT]
|
||||
.VirtualAddress) {
|
||||
const ImgDelayDescr *delayedImportDesc = static_cast<const ImgDelayDescr *>(
|
||||
rvaToPtr(delayedImportsStartRVA, ntHeaders, base));
|
||||
for (; delayedImportDesc->rvaDLLName && (delayedImportDesc->grAttrs & 1);
|
||||
++delayedImportDesc)
|
||||
result.push_back(
|
||||
stringFromRvaPtr(rvaToPtr(delayedImportDesc->rvaDLLName, ntHeaders, base)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class ImageNtHeader>
|
||||
static void determineDependentLibs(const ImageNtHeader *nth, const void *fileMemory,
|
||||
bool isMinGW,
|
||||
std::vector<std::string> *dependentLibrariesIn,
|
||||
std::wstring *errorMessage) {
|
||||
std::vector<std::string> dependentLibraries;
|
||||
if (dependentLibrariesIn)
|
||||
dependentLibraries = readImportSections(nth, fileMemory, errorMessage);
|
||||
if (dependentLibrariesIn)
|
||||
*dependentLibrariesIn = dependentLibraries;
|
||||
}
|
||||
|
||||
// Read a PE executable and determine dependent libraries, word size.
|
||||
bool readPeExecutable(const std::wstring &peExecutableFileName, std::wstring *errorMessage,
|
||||
std::vector<std::string> *dependentLibrariesIn, unsigned *wordSizeIn,
|
||||
bool isMinGW, unsigned short *machineArchIn) {
|
||||
bool result = false;
|
||||
HANDLE hFile = NULL;
|
||||
HANDLE hFileMap = NULL;
|
||||
void *fileMemory = 0;
|
||||
|
||||
if (dependentLibrariesIn)
|
||||
dependentLibrariesIn->clear();
|
||||
if (wordSizeIn)
|
||||
*wordSizeIn = 0;
|
||||
|
||||
do {
|
||||
// Create a memory mapping of the file
|
||||
hFile = CreateFileW(peExecutableFileName.data(), GENERIC_READ, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE || hFile == NULL) {
|
||||
std::wostringstream ss;
|
||||
ss << L"Cannot open \"" << peExecutableFileName << L"\": "
|
||||
<< winErrorMessage(::GetLastError());
|
||||
*errorMessage = ss.str();
|
||||
break;
|
||||
}
|
||||
|
||||
hFileMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
if (hFileMap == NULL) {
|
||||
std::wostringstream ss;
|
||||
ss << L"Cannot create file mapping of \"" << peExecutableFileName << L"\": "
|
||||
<< winErrorMessage(::GetLastError());
|
||||
*errorMessage = ss.str();
|
||||
break;
|
||||
}
|
||||
|
||||
fileMemory = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
|
||||
if (!fileMemory) {
|
||||
std::wostringstream ss;
|
||||
ss << L"Cannot map \"" << peExecutableFileName << L"\": "
|
||||
<< winErrorMessage(::GetLastError());
|
||||
*errorMessage = ss.str();
|
||||
break;
|
||||
}
|
||||
|
||||
const IMAGE_NT_HEADERS *ntHeaders = getNtHeader(fileMemory, errorMessage);
|
||||
if (!ntHeaders)
|
||||
break;
|
||||
|
||||
const unsigned wordSize = ntHeaderWordSize(ntHeaders);
|
||||
if (wordSizeIn)
|
||||
*wordSizeIn = wordSize;
|
||||
if (wordSize == 32) {
|
||||
determineDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS32 *>(ntHeaders),
|
||||
fileMemory, isMinGW, dependentLibrariesIn, errorMessage);
|
||||
} else {
|
||||
determineDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS64 *>(ntHeaders),
|
||||
fileMemory, isMinGW, dependentLibrariesIn, errorMessage);
|
||||
}
|
||||
|
||||
if (machineArchIn)
|
||||
*machineArchIn = ntHeaders->FileHeader.Machine;
|
||||
|
||||
result = true;
|
||||
} while (false);
|
||||
|
||||
if (fileMemory)
|
||||
UnmapViewOfFile(fileMemory);
|
||||
|
||||
if (hFileMap != NULL)
|
||||
CloseHandle(hFileMap);
|
||||
|
||||
if (hFile != NULL && hFile != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(hFile);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<std::wstring>
|
||||
resolveWinBinaryDependencies(const std::filesystem::path &path,
|
||||
const std::vector<std::filesystem::path> &searchingPaths,
|
||||
std::vector<std::string> *unparsed) {
|
||||
std::wstring errorMessage;
|
||||
std::vector<std::string> dependentLibrariesIn;
|
||||
unsigned wordSizeIn;
|
||||
bool isMinGW = false;
|
||||
unsigned short machineArchIn;
|
||||
if (!WindowsDeployQt::readPeExecutable(path, &errorMessage, &dependentLibrariesIn,
|
||||
&wordSizeIn, isMinGW, &machineArchIn)) {
|
||||
throw std::runtime_error(SCL::wideToUtf8(errorMessage));
|
||||
}
|
||||
|
||||
// Search
|
||||
std::vector<std::wstring> result;
|
||||
for (const auto &item : std::as_const(dependentLibrariesIn)) {
|
||||
fs::path fullPath;
|
||||
for (const auto &dir : std::as_const(searchingPaths)) {
|
||||
fs::path targetPath = dir / item;
|
||||
if (fs::exists(targetPath)) {
|
||||
fullPath = targetPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fullPath.empty()) {
|
||||
result.push_back(fullPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unparsed) {
|
||||
unparsed->push_back(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
13
libs/qwindowkit/qmsetup/src/qmsetupConfig.cmake.in
Normal file
13
libs/qwindowkit/qmsetup/src/qmsetupConfig.cmake.in
Normal file
@@ -0,0 +1,13 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/qmsetupTargets.cmake")
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cmake/QMSetupAPI.cmake")
|
||||
|
||||
if ("All" IN_LIST qmsetup_FIND_COMPONENTS)
|
||||
qm_import_all()
|
||||
else()
|
||||
qm_import(${qmsetup_FIND_COMPONENTS})
|
||||
endif()
|
||||
130
libs/qwindowkit/qmsetup/src/syscmdline/.clang-format
Normal file
130
libs/qwindowkit/qmsetup/src/syscmdline/.clang-format
Normal file
@@ -0,0 +1,130 @@
|
||||
# References:
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
# https://code.qt.io/cgit/qt/qt5.git/tree/_clang-format
|
||||
|
||||
BasedOnStyle: LLVM
|
||||
|
||||
Standard: c++17
|
||||
|
||||
# 指针和引用的对齐方式。
|
||||
# 可能的值有:
|
||||
# PAS_Left (在配置中: Left) 指针左对齐。
|
||||
# PAS_Right (在配置中: Right) 指针右对齐。
|
||||
# PAS_Middle (在配置中: Middle) 指针中间对齐。
|
||||
PointerAlignment: Right
|
||||
|
||||
# public/protected/private 等访问修饰符偏移量
|
||||
AccessModifierOffset: -4
|
||||
|
||||
# 缩进长度
|
||||
IndentWidth: 4
|
||||
|
||||
# 连续空行的最大数
|
||||
MaxEmptyLinesToKeep: 999
|
||||
|
||||
# 在OC中的@property后面添加一个空格。例如:使用“@property (readonly)”而不是“@property(readonly)”
|
||||
ObjCSpaceAfterProperty: true
|
||||
|
||||
# OC块中所拍的字符数
|
||||
ObjCBlockIndentWidth: 4
|
||||
|
||||
# 取决于值, 语句“int f() { return 0; }”可以被放到一个单行。
|
||||
# 可能的值有:
|
||||
# SFS_None (在配置中: None) 从不合并方法或函数到单独的一行。
|
||||
# SFS_Empty (在配置中: Empty) 仅合并空的函数。
|
||||
# SFS_Inline (在配置中: Inline) 仅合并类中定义的方法或函数. 意味着 “empty”.
|
||||
# SFS_All (在配置中: All) 合并所有的方法适应单行.
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
|
||||
# 如果为真(true), 语句“if (a) return;” 能被放到单行。
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
|
||||
# 如果为真(true), 对齐注释。
|
||||
AlignTrailingComments: true
|
||||
|
||||
# 如果为真,对齐连续的宏定义
|
||||
AlignConsecutiveMacros: true
|
||||
|
||||
# 如果为真(true),将会在“[”之后和“]”之前插入空格。
|
||||
SpacesInSquareBrackets: false
|
||||
|
||||
# 如果为真(true), 将会在“(”之后和“)”之前插入空格。
|
||||
SpacesInParentheses : false
|
||||
|
||||
# 如果为真(true), 校准连续的声明。
|
||||
# 这将会校准连续多行的声明的名字。这将会导致像下面这样的格式:
|
||||
# int aaaa = 12;
|
||||
# float b = 23;
|
||||
# std::string ccc = 23;
|
||||
AlignConsecutiveDeclarations: false
|
||||
|
||||
# 如果为真(true),连续调整多行
|
||||
# 这将会调整连续行中的分配操作符。这将会导致像下面这样的格式:
|
||||
# int aaaa = 12;
|
||||
# int b = 23;
|
||||
# int ccc = 23;
|
||||
AlignConsecutiveAssignments: false
|
||||
|
||||
# 如果为假(false),移除分配操作符(=)前空格。
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
|
||||
# 如果为真(true), 将会在字面量容器中插入空格(例如 OC和Javascript的数组和字典字面量)。
|
||||
SpacesInContainerLiterals: false
|
||||
|
||||
# 缩进case标签
|
||||
IndentCaseLabels: true
|
||||
|
||||
# 如果表达式中包含函数调用,并且函数调用因为表达式太长被放到了下一行,是否缩进
|
||||
IndentWrappedFunctionNames: true
|
||||
|
||||
# 如果为真(true), 保持块的起始空行。
|
||||
# true: false:
|
||||
# if (foo) { vs. if (foo) {
|
||||
# bar();
|
||||
# bar(); }
|
||||
# }
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
|
||||
# 允许所有参数都被放在下一行
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
|
||||
# 使用C风格强制类型转换后,是否在中间添加一个空格
|
||||
SpaceAfterCStyleCast: true
|
||||
|
||||
# 在模板定义后换行
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
|
||||
# Tab长度
|
||||
TabWidth: 4
|
||||
|
||||
# 是否使用Tab
|
||||
UseTab: Never
|
||||
|
||||
# 在括号后对齐参数
|
||||
# someLongFunction(argument1,
|
||||
# argument2);
|
||||
AlignAfterOpenBracket: Align
|
||||
|
||||
# 名字空间内部缩进
|
||||
NamespaceIndentation: All
|
||||
|
||||
# 一行最长列数
|
||||
ColumnLimit: 100
|
||||
|
||||
# 按层次缩进宏定义
|
||||
IndentPPDirectives: AfterHash
|
||||
|
||||
# 预处理语句缩进为 2
|
||||
PPIndentWidth: 2
|
||||
|
||||
# 数组元素对齐
|
||||
AlignArrayOfStructures: Left
|
||||
|
||||
# 不对头文件排序
|
||||
SortIncludes: Never
|
||||
|
||||
FixNamespaceComments: false
|
||||
|
||||
StatementMacros: ['__qas_attr__', '__qas_exclude__', '__qas_include__']
|
||||
|
||||
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ]
|
||||
98
libs/qwindowkit/qmsetup/src/syscmdline/.gitignore
vendored
Normal file
98
libs/qwindowkit/qmsetup/src/syscmdline/.gitignore
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
# This file is used to ignore files which are generated
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
*~
|
||||
*.log
|
||||
*.autosave
|
||||
*.a
|
||||
*.core
|
||||
*.moc
|
||||
*.o
|
||||
*.obj
|
||||
*.orig
|
||||
*.rej
|
||||
*.so
|
||||
*.so.*
|
||||
*_pch.h.cpp
|
||||
*_resource.rc
|
||||
*.qm
|
||||
.#*
|
||||
*.*#
|
||||
core
|
||||
!core/
|
||||
tags
|
||||
.DS_Store
|
||||
.directory
|
||||
*.debug
|
||||
Makefile*
|
||||
*.prl
|
||||
*.app
|
||||
moc_*.cpp
|
||||
ui_*.h
|
||||
qrc_*.cpp
|
||||
Thumbs.db
|
||||
# *.res
|
||||
# *.rc
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
|
||||
# qtcreator generated files
|
||||
*.pro.user*
|
||||
|
||||
# xemacs temporary files
|
||||
*.flc
|
||||
|
||||
# Vim temporary files
|
||||
.*.swp
|
||||
|
||||
# Visual Studio generated files
|
||||
*.ib_pdb_index
|
||||
*.idb
|
||||
*.ilk
|
||||
*.pdb
|
||||
*.sln
|
||||
*.suo
|
||||
*.vcproj
|
||||
*vcproj.*.*.user
|
||||
*.ncb
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.vcxproj
|
||||
*vcxproj.*
|
||||
|
||||
# MinGW generated files
|
||||
*.Debug
|
||||
*.Release
|
||||
|
||||
# Python byte code
|
||||
__pycache__
|
||||
*.pyc
|
||||
|
||||
# Binaries
|
||||
# --------
|
||||
*.dll
|
||||
*.exe
|
||||
|
||||
.DS_Store
|
||||
*/.DS_Store
|
||||
|
||||
build-src*
|
||||
build/
|
||||
cmake-build*
|
||||
*.user
|
||||
*.lnk
|
||||
_workingDir*
|
||||
.vscode
|
||||
.idea
|
||||
.cache
|
||||
cache
|
||||
.vs
|
||||
out/
|
||||
CMakeSettings.json
|
||||
# /vcpkg
|
||||
/data
|
||||
/*.natvis
|
||||
|
||||
*.sublime-*
|
||||
setup-vcpkg.json
|
||||
setup-vcpkg-temp*
|
||||
165
libs/qwindowkit/qmsetup/src/syscmdline/CMakeLists.txt
Normal file
165
libs/qwindowkit/qmsetup/src/syscmdline/CMakeLists.txt
Normal file
@@ -0,0 +1,165 @@
|
||||
cmake_minimum_required(VERSION 3.17)
|
||||
|
||||
project(syscmdline VERSION 1.0.0.0 LANGUAGES C CXX)
|
||||
|
||||
# ----------------------------------
|
||||
# Build Options
|
||||
# ----------------------------------
|
||||
option(SYSCMDLINE_BUILD_STATIC "Build static library" ON)
|
||||
option(SYSCMDLINE_BUILD_EXAMPLES "Build examples" OFF)
|
||||
option(SYSCMDLINE_BUILD_TESTS "Build test cases" OFF)
|
||||
option(SYSCMDLINE_FORCE_VALIDITY_CHECK "Force to enable validity check" OFF)
|
||||
option(SYSCMDLINE_INSTALL "Install library" ON)
|
||||
|
||||
# ----------------------------------
|
||||
# CMake Settings
|
||||
# ----------------------------------
|
||||
if(NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_BUILD_SHARE_DIR)
|
||||
set(CMAKE_BUILD_SHARE_DIR ${CMAKE_BINARY_DIR}/share)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /manifest:no")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /manifest:no")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /manifest:no")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8")
|
||||
|
||||
if(NOT DEFINED CMAKE_DEBUG_POSTFIX)
|
||||
set(CMAKE_DEBUG_POSTFIX "d")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(SYSCMDLINE_INSTALL)
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
endif()
|
||||
|
||||
# ----------------------------------
|
||||
# Project Variables
|
||||
# ----------------------------------
|
||||
set(SYSCMDLINE_VERSION ${PROJECT_VERSION})
|
||||
set(SYSCMDLINE_INSTALL_NAME ${PROJECT_NAME})
|
||||
|
||||
# ----------------------------------
|
||||
# Main Project
|
||||
# ----------------------------------
|
||||
if(TRUE)
|
||||
# Add library
|
||||
if(SYSCMDLINE_BUILD_STATIC)
|
||||
add_library(${PROJECT_NAME} STATIC)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC SYSCMDLINE_STATIC)
|
||||
else()
|
||||
add_library(${PROJECT_NAME} SHARED)
|
||||
endif()
|
||||
|
||||
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||
|
||||
# Add sources
|
||||
file(GLOB_RECURSE _src include/syscmdline/*.h src/*.h src/*.cpp)
|
||||
target_sources(${PROJECT_NAME} PRIVATE ${_src})
|
||||
|
||||
# Add features
|
||||
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20 cxx_std_17)
|
||||
|
||||
# Add options
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE SYSCMDLINE_LIBRARY)
|
||||
|
||||
if(SYSCMDLINE_FORCE_VALIDITY_CHECK)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE SYSCMDLINE_ENABLE_VALIDITY_CHECK)
|
||||
else()
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||
"$<$<CONFIG:Debug>:SYSCMDLINE_ENABLE_VALIDITY_CHECK>"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Include directories
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||
)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE include/syscmdline)
|
||||
|
||||
# Add platform specific
|
||||
if(WIN32)
|
||||
set(RC_DESCRIPTION "Another C++ Command Line Parser")
|
||||
set(RC_COPYRIGHT "Copyright 2023 SineStriker")
|
||||
include("cmake/winrc.cmake")
|
||||
endif()
|
||||
|
||||
# if(MSVC)
|
||||
# if(${CMAKE_BUILD_TYPE} STREQUAL "Release")
|
||||
# target_compile_options(${PROJECT_NAME} PRIVATE /GL /Os)
|
||||
# target_link_options(${PROJECT_NAME} PRIVATE /OPT:REF /OPT:ICF /OPT:LBR)
|
||||
# endif()
|
||||
# endif()
|
||||
if(SYSCMDLINE_INSTALL)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
)
|
||||
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
EXPORT ${SYSCMDLINE_INSTALL_NAME}Targets
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" OPTIONAL
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" OPTIONAL
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(SYSCMDLINE_BUILD_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if(SYSCMDLINE_BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
if(SYSCMDLINE_INSTALL)
|
||||
# Add install target
|
||||
set(_install_dir ${CMAKE_INSTALL_LIBDIR}/cmake/${SYSCMDLINE_INSTALL_NAME})
|
||||
|
||||
# Add version file
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${SYSCMDLINE_INSTALL_NAME}ConfigVersion.cmake"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
# Add configuration file
|
||||
configure_package_config_file(
|
||||
${CMAKE_CURRENT_LIST_DIR}/${SYSCMDLINE_INSTALL_NAME}Config.cmake.in
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${SYSCMDLINE_INSTALL_NAME}Config.cmake"
|
||||
INSTALL_DESTINATION ${_install_dir}
|
||||
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
||||
)
|
||||
|
||||
# Install cmake files
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${SYSCMDLINE_INSTALL_NAME}Config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${SYSCMDLINE_INSTALL_NAME}ConfigVersion.cmake"
|
||||
DESTINATION ${_install_dir}
|
||||
)
|
||||
|
||||
# Install cmake targets files
|
||||
install(EXPORT ${SYSCMDLINE_INSTALL_NAME}Targets
|
||||
FILE "${SYSCMDLINE_INSTALL_NAME}Targets.cmake"
|
||||
NAMESPACE syscmdline::
|
||||
DESTINATION ${_install_dir}
|
||||
)
|
||||
|
||||
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
FILES_MATCHING PATTERN "*.h"
|
||||
)
|
||||
endif()
|
||||
21
libs/qwindowkit/qmsetup/src/syscmdline/LICENSE
Normal file
21
libs/qwindowkit/qmsetup/src/syscmdline/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 SineStriker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
186
libs/qwindowkit/qmsetup/src/syscmdline/README.md
Normal file
186
libs/qwindowkit/qmsetup/src/syscmdline/README.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# SysCmdLine
|
||||
|
||||
Another C++ Command Line Parser.
|
||||
|
||||
Inspired by Qt `QCommandLineParser` and C# `System.CommandLine`.
|
||||
|
||||
## Introduction
|
||||
|
||||
To be in line with the principle of "don't learn anything new if you don't need to", on the one hand, `syscmdline` contains as many common features as possible, on the other hand, it's simple enough to be easy to understand.
|
||||
|
||||
Therefore, the project is designed to be configurable, but it's not intended to be as complex as a framework.
|
||||
|
||||
## Features
|
||||
|
||||
+ Support sub-commands
|
||||
+ Support case-insensitive parsing
|
||||
+ Support global options
|
||||
+ Support mutually exclusive options
|
||||
+ Support short options and group flags
|
||||
+ Support help text customization
|
||||
+ Support localization
|
||||
+ Simple tips for typo correction
|
||||
+ Special implementations for Windows
|
||||
+ Highly configurable
|
||||
+ Friendly interface
|
||||
|
||||
|
||||
<!-- ## Help Text
|
||||
|
||||
The help text is highly configurable, here we show some simple ones.
|
||||
|
||||
### Unix Style
|
||||
```
|
||||
Description:
|
||||
Move source(s) to destination directory.
|
||||
|
||||
Usage:
|
||||
mv <src>... <dest> [options]
|
||||
|
||||
Arguments:
|
||||
src Source(s)
|
||||
dest Destination directory
|
||||
|
||||
Options:
|
||||
-v, --version Show version information
|
||||
-h, --help Show help information
|
||||
```
|
||||
### Dos Style
|
||||
```
|
||||
Description:
|
||||
Deletes one or more files.
|
||||
|
||||
Usage:
|
||||
del files... [/P] [/F] [/S] [/Q] [/?]
|
||||
|
||||
Arguments:
|
||||
files Specifies a list of one or more files or directories.
|
||||
|
||||
Options:
|
||||
/P Prompts for confirmation before deleting the specified file.
|
||||
/F Forces deletion of read-only files.
|
||||
/S Deletes specified files from the current directory and all subdirectories.
|
||||
/Q Specifies quiet mode. You are not prompted for delete confirmation.
|
||||
/? Show help information.
|
||||
``` -->
|
||||
|
||||
## Simple Example
|
||||
|
||||
A simple `mv` command:
|
||||
```c++
|
||||
#include <iostream>
|
||||
#include <syscmdline/parser.h>
|
||||
|
||||
namespace SCL = SysCmdLine;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
SCL::Command cmd("mv", "move files to directory");
|
||||
cmd.addArguments({
|
||||
SCL::Argument("files", "Source files").multi(),
|
||||
SCL::Argument("dir", "Destination directory"),
|
||||
});
|
||||
cmd.addHelpOption();
|
||||
cmd.setHandler([](const SCL::ParseResult &result) -> int {
|
||||
std::cout << "[Sources]" << std::endl;
|
||||
for (const auto &item : result.values("files")) {
|
||||
std::cout << item.toString() << std::endl;
|
||||
}
|
||||
std::cout << "[Destination]" << std::endl;
|
||||
std::cout << result.value("dir").toString() << std::endl;
|
||||
return 0;
|
||||
});
|
||||
return SCL::Parser(cmd).invoke(argc, argv);
|
||||
}
|
||||
```
|
||||
Running the code:
|
||||
```sh
|
||||
> ./mv --help
|
||||
Description:
|
||||
move files to directory
|
||||
|
||||
Usage:
|
||||
mv <files>... <dir> [options]
|
||||
|
||||
Arguments:
|
||||
<files> Source files
|
||||
<dir> Destination directory
|
||||
|
||||
Options:
|
||||
-h, --help Show help information
|
||||
```
|
||||
```sh
|
||||
> ./mv 1 2
|
||||
[Sources]
|
||||
1
|
||||
[Destination]
|
||||
2
|
||||
```
|
||||
```sh
|
||||
>./mv 1 2 3
|
||||
[Sources]
|
||||
1
|
||||
2
|
||||
[Destination]
|
||||
3
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Concepts
|
||||
|
||||
If you are confused about some of the concepts of command line programs, you can learn the following, which will help you use this project.
|
||||
|
||||
See [Concepts](docs/concepts.md) to learn more.
|
||||
|
||||
### More Examples
|
||||
|
||||
See [Examples](docs/examples.md) to learn more.
|
||||
|
||||
## CMake Intergration
|
||||
|
||||
### Build & Install
|
||||
|
||||
```sh
|
||||
cmake -B build -G Ninja
|
||||
cmake --build build --target all
|
||||
cmake --build build --target install
|
||||
```
|
||||
|
||||
### Import
|
||||
|
||||
```cmake
|
||||
find_package(syscmdline REQUIRED)
|
||||
target_link_libraries(my_project PRIVATE syscmdline::syscmdline)
|
||||
```
|
||||
|
||||
## Notice
|
||||
|
||||
<!-- + C++ Standard
|
||||
+ You need a C++ compiler which supports C++17 to build the library. The interface is compatible with C++11. -->
|
||||
|
||||
+ Minimize Size
|
||||
+ In order to achieve more functionalities, this project contains a large amount of codes so that the binary size may be relatively large compared with other libraries. Therefore, this implementation uses STL templates as little as possible.
|
||||
+ It's suggested to enable size optimizing option for your compiler when building executables.
|
||||
|
||||
+ Validity Check
|
||||
+ The root command must be valid, otherwise the parsing result is undefined and may even cause crash.
|
||||
+ Validity checking is enabled if `SYSCMDLINE_ENABLE_VALIDITY_CHECK` is defined, which reduces parsing performance. Therefore, this macro is enabled only in debug mode.
|
||||
|
||||
## Thanks
|
||||
|
||||
### Other Projects
|
||||
|
||||
+ https://github.com/qt/qtbase
|
||||
+ https://github.com/dotnet/command-line-api
|
||||
<!-- + https://github.com/tanakh/cmdline
|
||||
+ https://github.com/p-ranav/argparse
|
||||
+ https://github.com/CLIUtils/CLI11 -->
|
||||
|
||||
### Contributors
|
||||
|
||||
+ [wangwenx190](https://github.com/wangwenx190)
|
||||
+ [JobSecond](https://github.com/JobSecond)
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
34
libs/qwindowkit/qmsetup/src/syscmdline/cmake/winrc.cmake
Normal file
34
libs/qwindowkit/qmsetup/src/syscmdline/cmake/winrc.cmake
Normal file
@@ -0,0 +1,34 @@
|
||||
set(_rc_content "#include <windows.h>
|
||||
|
||||
#ifndef VS_VERSION_INFO
|
||||
#define VS_VERSION_INFO 1
|
||||
#endif
|
||||
|
||||
#define _STRINGIFY(x) #x
|
||||
#define STRINGIFY(x) _STRINGIFY(x)
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},${PROJECT_VERSION_TWEAK}
|
||||
PRODUCTVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},${PROJECT_VERSION_TWEAK}
|
||||
{
|
||||
BLOCK \"StringFileInfo\"
|
||||
{
|
||||
// U.S. English - Windows, Multilingual
|
||||
BLOCK \"040904E4\"
|
||||
{
|
||||
VALUE \"FileDescription\", STRINGIFY(${RC_DESCRIPTION})
|
||||
VALUE \"FileVersion\", STRINGIFY(${PROJECT_VERSION})
|
||||
VALUE \"ProductName\", STRINGIFY(${PROJECT_NAME})
|
||||
VALUE \"ProductVersion\", STRINGIFY(${PROJECT_VERSION})
|
||||
VALUE \"LegalCopyright\", STRINGIFY(${RC_COPYRIGHT})
|
||||
}
|
||||
}
|
||||
BLOCK \"VarFileInfo\"
|
||||
{
|
||||
VALUE \"Translation\", 0x409, 1252 // 1252 = 0x04E4
|
||||
}
|
||||
}")
|
||||
|
||||
set(_rc_file ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_res.rc)
|
||||
file(WRITE ${_rc_file} ${_rc_content})
|
||||
target_sources(${PROJECT_NAME} PRIVATE ${_rc_file})
|
||||
23
libs/qwindowkit/qmsetup/src/syscmdline/docs/concepts.md
Normal file
23
libs/qwindowkit/qmsetup/src/syscmdline/docs/concepts.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Concepts
|
||||
|
||||
## Command
|
||||
|
||||
+ **Command** is the beginning symbol used to determine the behavior of a set of command-line arguments.
|
||||
|
||||
+ The commands of a CLI program are usually in the form of a tree structure, and the program itself is the root command. Take `git` as an example, `git` is the root command and `merge`/`commit`/... are the sub-commands.
|
||||
|
||||
+ However, most commands doesn't have sub-commands because the root command is sufficient in most scenarios.
|
||||
|
||||
+ Command line parameters must start with the root command and list the subcommands in sequence along the path to a certain leaf command. When the target sub-command is listed, all subsequent symbols are regarded as the arguments or options of this sub-command.
|
||||
|
||||
## Argument
|
||||
|
||||
+ **Argument** is the positional argument, which belongs to a command or an option.
|
||||
|
||||
+ The positional arguments are parsed according to the order in which they are specified. Take `mv` as an example, `mv <src> <dest>` shows that it has two positional arguments and the first will be recognized as source and the second as destination.
|
||||
|
||||
## Option
|
||||
|
||||
+ **Option**, usually a word or letter beginning with the `-` or `/` sign. (`-` is the Unix style and `/` is the Dos style)
|
||||
|
||||
+ Options can have none or a fixed number of positional arguments which are recommended to be non-required.
|
||||
251
libs/qwindowkit/qmsetup/src/syscmdline/docs/examples.md
Normal file
251
libs/qwindowkit/qmsetup/src/syscmdline/docs/examples.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# Examples
|
||||
|
||||
## Basic
|
||||
|
||||
+ Acquire Program Arguments
|
||||
+ Positional Argument
|
||||
+ Optinal Argument And Restricted Argument
|
||||
+ Option
|
||||
+ Multi-argument Option
|
||||
+ Multi-value Argument
|
||||
|
||||
### Acquire Program Arguments
|
||||
|
||||
The most common way to get command-line arguments is directly from the arguments of the `main` function.
|
||||
|
||||
```c
|
||||
int main(int argc, char *argv[]);
|
||||
```
|
||||
If your host system is Unix, then everything is fine. However, this set of arguments from `main` is likely to cause problems on Windows because they are in ANSI encoding.
|
||||
|
||||
On Windows, we must call `GetCommandLineW()` to get the correct UTF-16 encoded arguments.
|
||||
|
||||
As a result, `syscmdline` provides a generic way to get command-line arguments that does not depend on the `main` function.
|
||||
|
||||
+ On Windows, it acquires arguments by calling `GetCommandLineW()` and converts the result to UTF-8 string list.
|
||||
|
||||
+ On Mac, it acquires arguments by calling `_NSGetArgv`.
|
||||
|
||||
+ On Linux, it reads the arguments from `/proc/self/cmdline`.
|
||||
|
||||
```c++
|
||||
namespace SysCmdLine {
|
||||
std::vector<std::string> commandLineArguments();
|
||||
}
|
||||
```
|
||||
Include `system.h` to import this function.
|
||||
```c++
|
||||
#include <syscmdline/system.h>
|
||||
```
|
||||
|
||||
If you are developing a program on Windows, we recommend using this function to get command line arguments.
|
||||
|
||||
`syscmdline` uses UTF-8 internally to output to the console, error may occur if the ANSI-encoded arguments contain non-ASCII characters.
|
||||
|
||||
### Positional Argument
|
||||
|
||||
Simple Example of one positional argument:
|
||||
|
||||
```c++
|
||||
#include <iostream>
|
||||
|
||||
#include <syscmdline/parser.h>
|
||||
#include <syscmdline/system.h>
|
||||
|
||||
namespace SCL = SysCmdLine;
|
||||
|
||||
int main(int /* argc */, char * /* argv */ []) {
|
||||
SCL::Argument intArg("int", "Integer");
|
||||
intArg.setDefaultValue(0);
|
||||
|
||||
SCL::Command rootCommand("square", "Display the square of the specified integer.");
|
||||
rootCommand.addArgument(intArg);
|
||||
rootCommand.addHelpOption();
|
||||
rootCommand.setHandler([](const SCL::ParseResult &result) {
|
||||
auto num = result.value("int").toInt();
|
||||
std::cout << (num * num) << std::endl;
|
||||
return 0;
|
||||
});
|
||||
|
||||
SCL::Parser parser(rootCommand);
|
||||
return parser.invoke(SCL::commandLineArguments());
|
||||
}
|
||||
```
|
||||
+ In this case, we call `addArgument` to add one positional argument.
|
||||
|
||||
+ The `Argument` instance is default to be required, and we set an implicit defualt value to force the parser to to accept only numeric input.
|
||||
|
||||
Help text:
|
||||
```
|
||||
Description:
|
||||
Display the square of a given integer.
|
||||
|
||||
Usage:
|
||||
square <int> [options]
|
||||
|
||||
Arguments:
|
||||
<int> Integer
|
||||
|
||||
Options:
|
||||
-h, --help Show help information
|
||||
```
|
||||
|
||||
Test:
|
||||
```sh
|
||||
> ./square 32
|
||||
1024
|
||||
```
|
||||
|
||||
### Optinal Argument And Restricted Argument
|
||||
|
||||
Simple example of one restricted argument and one optional argument:
|
||||
|
||||
```c++
|
||||
int main(int /* argc */, char * /* argv */ []) {
|
||||
SCL::Argument weekdayArg("weekday", "Weekday");
|
||||
weekdayArg.setExpectedValues({
|
||||
"Monday", "Tuesday", "Wednesday", "Thursday",
|
||||
"Friday", "Saturday", "Sunday",
|
||||
});
|
||||
|
||||
SCL::Argument eventArg("event", "Event to do");
|
||||
eventArg.setDefaultValue("football");
|
||||
eventArg.setRequired(false);
|
||||
|
||||
SCL::Command rootCommand("work", "What to do on which day?");
|
||||
rootCommand.addArguments({weekdayArg, eventArg});
|
||||
rootCommand.addHelpOption();
|
||||
rootCommand.setHandler([](const SCL::ParseResult &result) {
|
||||
std::cout << result.value("weekday").toString() << std::endl;
|
||||
std::cout << result.value("event").toString() << std::endl;
|
||||
return 0;
|
||||
});
|
||||
|
||||
SCL::Parser parser(rootCommand);
|
||||
parser.setDisplayOptions(SCL::Parser::ShowArgumentDefaultValue |
|
||||
SCL::Parser::ShowArgumentExpectedValues);
|
||||
return parser.invoke(SCL::commandLineArguments());
|
||||
}
|
||||
```
|
||||
+ In this case, we add a restricted argument and another required argument with default value.
|
||||
|
||||
+ If an argument has an expect value list, only symbols listed in it will be regarded as valid argument.
|
||||
|
||||
+ We also tell the parser to show default value and expect values on help list which would be hided if the flags weren't set.
|
||||
|
||||
<!-- We could simplify the above code by using the self-return API:
|
||||
```c++
|
||||
int main(int /* argc */, char * /* argv */ []) {
|
||||
SCL::Command rootCommand("work", "What to do on which day?");
|
||||
rootCommand.addArguments({
|
||||
SCL::Argument("weekday", "Weekday").expect({
|
||||
"Monday", "Tuesday", "Wednesday", "Thursday",
|
||||
"Friday", "Saturday", "Sunday",
|
||||
}),
|
||||
SCL::Argument("event", "Event to do", false, "football"),
|
||||
});
|
||||
rootCommand.help().action([](const SCL::ParseResult &result) {
|
||||
std::cout << result.value("weekday").toString() << std::endl;
|
||||
std::cout << result.value("event").toString() << std::endl;
|
||||
return 0;
|
||||
});
|
||||
|
||||
SCL::Parser parser(rootCommand);
|
||||
parser.setDisplayOptions(SCL::Parser::ShowArgumentDefaultValue |
|
||||
SCL::Parser::ShowArgumentExpectedValues);
|
||||
return parser.invoke(SCL::commandLineArguments());
|
||||
}
|
||||
``` -->
|
||||
|
||||
Help Text:
|
||||
```
|
||||
Description:
|
||||
What to do on which day?
|
||||
|
||||
Usage:
|
||||
work <weekday> [<event>] [options]
|
||||
|
||||
Arguments:
|
||||
<weekday> Weekday [Expected: "Monday", "Tuesday", "Wednesday", ...]
|
||||
<event> Event to do [Default: football]
|
||||
|
||||
Options:
|
||||
-h, --help Show help information
|
||||
```
|
||||
Test:
|
||||
```sh
|
||||
> ./work Monday
|
||||
Monday
|
||||
football
|
||||
```
|
||||
```sh
|
||||
> ./work Tuesday basketball
|
||||
Tuesday
|
||||
basketball
|
||||
```
|
||||
|
||||
### Option
|
||||
|
||||
Simple example of a converter:
|
||||
```c++
|
||||
int main(int /* argc */, char * /* argv */ []) {
|
||||
SCL::Option inputOpt("--input", "Input file");
|
||||
inputOpt.addArgument(SCL::Argument("file"));
|
||||
inputOpt.setRequired(true);
|
||||
|
||||
SCL::Option outputOpt("--output", "Output file");
|
||||
outputOpt.addArgument(SCL::Argument("file"));
|
||||
|
||||
SCL::Command rootCommand("converter", "Convert A to B.");
|
||||
rootCommand.addOptions({inputOpt, outputOpt});
|
||||
rootCommand.addHelpOption();
|
||||
rootCommand.setHandler([](const SCL::ParseResult &result) {
|
||||
std::cout << "Input: " << result.valueForOption("--input").toString() << std::endl;
|
||||
if (result.isOptionSet("--output")) {
|
||||
std::cout << "Output: " << result.valueForOption("--output").toString() << std::endl;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
SCL::Parser parser(rootCommand);
|
||||
parser.setDisplayOptions(SCL::Parser::ShowOptionalOptionsOnUsage);
|
||||
return parser.invoke(SCL::commandLineArguments());
|
||||
}
|
||||
```
|
||||
+ In this case, we add two single argument options `input` and `output`, and `input` is required.
|
||||
|
||||
+ We also tell the parser to show verbose options on usage.
|
||||
|
||||
Help Text:
|
||||
```
|
||||
Description:
|
||||
Convert A to B.
|
||||
|
||||
Usage:
|
||||
converter --input <file> [--output <file>] [-h]
|
||||
|
||||
Options:
|
||||
--input <file> Input file
|
||||
--output <file> Output file
|
||||
-h, --help Show help information
|
||||
```
|
||||
|
||||
Test:
|
||||
```sh
|
||||
> ./converter --input in
|
||||
Input: in
|
||||
```
|
||||
|
||||
```sh
|
||||
> ./converter --input in --output out
|
||||
Input: in
|
||||
Output: out
|
||||
```
|
||||
|
||||
### Multi-argument Option
|
||||
|
||||
TODO
|
||||
|
||||
### Multi-value Argument
|
||||
|
||||
TODO
|
||||
@@ -0,0 +1,4 @@
|
||||
add_subdirectory(del)
|
||||
add_subdirectory(mv)
|
||||
add_subdirectory(git)
|
||||
add_subdirectory(gcc)
|
||||
@@ -0,0 +1,7 @@
|
||||
project(del)
|
||||
|
||||
file(GLOB _src *.h *.cpp)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${_src})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE syscmdline)
|
||||
111
libs/qwindowkit/qmsetup/src/syscmdline/examples/del/main.cpp
Normal file
111
libs/qwindowkit/qmsetup/src/syscmdline/examples/del/main.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include <syscmdline/parser.h>
|
||||
#include <syscmdline/system.h>
|
||||
|
||||
namespace zh_CN {
|
||||
|
||||
static const char *error_strings[] = {
|
||||
R"(没有错误。)",
|
||||
R"(未知的选项 "%1"。)",
|
||||
R"(未知的命令或参数 "%1"。)",
|
||||
R"(选项 "%2" 缺少必选参数 "%1"。)",
|
||||
R"(缺少必选参数 "%1"。)",
|
||||
R"(参数过多。)",
|
||||
R"(参数 "%2" 被指定非法的值 "%1"。)",
|
||||
R"(选项 "%1" 出现在非法的位置,此处应为参数 "%2"。)",
|
||||
R"(缺少必选选项 "%1"。)",
|
||||
R"(选项 "%1" 出现次数过多,最多 %2 次。)",
|
||||
R"(参数 "%2" 被指定非法的值 "%1",应为 "%3"。)",
|
||||
R"(参数 "%2" 被指定非法的值 "%1",原因: %3)",
|
||||
R"(选项 "%1" 与 "%2" 是互斥的。)",
|
||||
R"(选项 "%1" 与其他参数不能同时指定。)",
|
||||
R"(选项 "%1" 与其他选项不能同时指定。)",
|
||||
R"(无法读取响应文件 "%1")",
|
||||
};
|
||||
|
||||
static const char *title_strings[] = {
|
||||
"错误", "用法", "简介", "参数", "选项", "命令", "必选", "默认", "合法值",
|
||||
};
|
||||
|
||||
static const char *role_strings[] = {
|
||||
"", "调试模式", "显示详细信息", "显示版本信息", "显示帮助信息",
|
||||
};
|
||||
|
||||
static const char *info_strings[] = {
|
||||
R"("%1" 未匹配。您是否想指定以下之一?)",
|
||||
};
|
||||
|
||||
static const char **strings[] = {
|
||||
error_strings,
|
||||
title_strings,
|
||||
role_strings,
|
||||
info_strings,
|
||||
};
|
||||
|
||||
static std::string provider(int category, int index) {
|
||||
return strings[category][index];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using namespace SysCmdLine;
|
||||
|
||||
static int routine(const ParseResult &result) {
|
||||
auto fileValues = Value::toStringList(result.values("files"));
|
||||
u8info("将要被删除的文件:\n");
|
||||
for (const auto &item : std::as_const(fileValues)) {
|
||||
u8info(" %s\n", item.data());
|
||||
}
|
||||
|
||||
bool prompt = result.isOptionSet("/P");
|
||||
bool force = result.isOptionSet("/F");
|
||||
bool subdir = result.isOptionSet("/S");
|
||||
bool quiet = result.isOptionSet("/Q");
|
||||
|
||||
u8info("模式: \n");
|
||||
if (prompt) {
|
||||
u8info(" 提示\n");
|
||||
}
|
||||
if (force) {
|
||||
u8info(" 强制\n");
|
||||
}
|
||||
if (subdir) {
|
||||
u8info(" 子文件夹\n");
|
||||
}
|
||||
if (quiet) {
|
||||
u8info(" 静默\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
SYSCMDLINE_UNUSED(argc);
|
||||
SYSCMDLINE_UNUSED(argv);
|
||||
|
||||
Option promptOption({"/P", "-p"}, "删除每一个文件之前提示确认");
|
||||
Option forceOption({"/F", "-f"}, "强制删除只读文件");
|
||||
Option subdirOption({"/S", "-s"}, "删除所有子目录中的指定的文件");
|
||||
Option quietOption({"/Q", "-q"}, "安静模式。删除全局通配符时,不要求确认");
|
||||
|
||||
Argument fileArg("files", "指定一个或多个文件或者目录列表");
|
||||
fileArg.setDisplayName("files");
|
||||
fileArg.setMultiValueEnabled(true);
|
||||
|
||||
Command rootCommand("del", {});
|
||||
rootCommand.addArgument(fileArg);
|
||||
rootCommand.addOptions({
|
||||
promptOption,
|
||||
forceOption,
|
||||
subdirOption,
|
||||
quietOption,
|
||||
});
|
||||
rootCommand.addHelpOption(false, false, {"/?"});
|
||||
rootCommand.setHandler(routine);
|
||||
|
||||
Parser parser(rootCommand);
|
||||
parser.setTextProvider(zh_CN::provider);
|
||||
parser.setDisplayOptions(Parser::ShowOptionalOptionsOnUsage | Parser::ShowArgumentIsRequired |
|
||||
Parser::AlignAllCatalogues);
|
||||
return parser.invoke(commandLineArguments(), -1,
|
||||
Parser::IgnoreOptionCase | Parser::AllowUnixGroupFlags);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
project(gcc)
|
||||
|
||||
file(GLOB _src *.h *.cpp)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${_src})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE syscmdline)
|
||||
36
libs/qwindowkit/qmsetup/src/syscmdline/examples/gcc/main.cpp
Normal file
36
libs/qwindowkit/qmsetup/src/syscmdline/examples/gcc/main.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <syscmdline/parser.h>
|
||||
#include <syscmdline/system.h>
|
||||
|
||||
using namespace SysCmdLine;
|
||||
|
||||
int main(int /* argc */, char * /* argv */[]) {
|
||||
Option linkDirOption({"-L", "--linkdir"}, "Add link directory");
|
||||
linkDirOption.setShortMatchRule(Option::ShortMatchSingleChar);
|
||||
linkDirOption.addArgument(Argument("dir"));
|
||||
linkDirOption.setUnlimitedOccurrence();
|
||||
|
||||
Option includeDirOption({"-I", "--includedir"}, "Add include directory");
|
||||
includeDirOption.setShortMatchRule(Option::ShortMatchSingleChar);
|
||||
includeDirOption.addArgument(Argument("dir"));
|
||||
includeDirOption.setUnlimitedOccurrence();
|
||||
|
||||
Command rootCommand("gcc", "GNU C/C++ Compiler");
|
||||
rootCommand.addOptions({
|
||||
linkDirOption,
|
||||
includeDirOption,
|
||||
});
|
||||
rootCommand.addVersionOption("0.0.0.1");
|
||||
rootCommand.addHelpOption(true, true);
|
||||
rootCommand.setHandler([](const ParseResult &result) {
|
||||
u8info("Link directories:\n");
|
||||
for (const auto &item : result.option("-L").allValues())
|
||||
u8info(" %s\n", item.toString().data());
|
||||
u8info("Include directories:\n");
|
||||
for (const auto &item : result.option("-I").allValues())
|
||||
u8info(" %s\n", item.toString().data());
|
||||
return 0;
|
||||
});
|
||||
|
||||
Parser parser(rootCommand);
|
||||
return parser.invoke(commandLineArguments());
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
project(git)
|
||||
|
||||
file(GLOB _src *.h *.cpp)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${_src})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE syscmdline)
|
||||
39
libs/qwindowkit/qmsetup/src/syscmdline/examples/git/main.cpp
Normal file
39
libs/qwindowkit/qmsetup/src/syscmdline/examples/git/main.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <syscmdline/parser.h>
|
||||
#include <syscmdline/system.h>
|
||||
|
||||
using namespace SysCmdLine;
|
||||
|
||||
int main(int /* argc */, char * /* argv */[]) {
|
||||
Command cloneCommand("clone", "Clone a repository into a new directory");
|
||||
Command initCommand("init", "Create an empty Git repository or reinitialize an existing one");
|
||||
|
||||
Command commitCommand("commit", "Record changes to the repository");
|
||||
Command mergeCommand("merge", "Join two or more development histories together");
|
||||
Command rebaseCommand("rebase", "Reapply commits on top of another base tip");
|
||||
|
||||
Command rootCommand("git", "Git is a distributed version control system.");
|
||||
rootCommand.addCommands({
|
||||
cloneCommand,
|
||||
initCommand,
|
||||
commitCommand,
|
||||
mergeCommand,
|
||||
rebaseCommand,
|
||||
});
|
||||
rootCommand.addVersionOption("0.0.0.1");
|
||||
rootCommand.addHelpOption(true, true);
|
||||
rootCommand.setHandler([](const ParseResult &result) {
|
||||
u8info("%s\n", result.value("weekday").toString().data());
|
||||
u8info("%s\n", result.value("event").toString().data());
|
||||
return 0;
|
||||
});
|
||||
|
||||
CommandCatalogue cc;
|
||||
cc.addCommands("start a working area", {"clone", "init"});
|
||||
cc.addCommands("grow, mark and tweak your common history", {"commit", "merge", "rebase"});
|
||||
rootCommand.setCatalogue(cc);
|
||||
|
||||
Parser parser(rootCommand);
|
||||
parser.setDisplayOptions(Parser::ShowArgumentDefaultValue | Parser::ShowArgumentExpectedValues |
|
||||
Parser::DontShowHelpOnError | Parser::AlignAllCatalogues);
|
||||
return parser.invoke(SysCmdLine::commandLineArguments());
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
project(mv)
|
||||
|
||||
file(GLOB _src *.h *.cpp)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${_src})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE syscmdline)
|
||||
24
libs/qwindowkit/qmsetup/src/syscmdline/examples/mv/main.cpp
Normal file
24
libs/qwindowkit/qmsetup/src/syscmdline/examples/mv/main.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include <iostream>
|
||||
#include <syscmdline/parser.h>
|
||||
using namespace SysCmdLine;
|
||||
int main(int argc, char *argv[]) {
|
||||
Command cmd("mv", "move files to directory");
|
||||
cmd.addArguments({
|
||||
Argument("files", "Source files").multi(),
|
||||
Argument("dir", "Destination directory"),
|
||||
});
|
||||
cmd.addHelpOption();
|
||||
cmd.setHandler([](const ParseResult &result) -> int {
|
||||
std::cout << "[Sources]" << std::endl;
|
||||
for (const auto &item : result.values("files")) {
|
||||
std::cout << item.toString() << std::endl;
|
||||
}
|
||||
std::cout << "[Destination]" << std::endl;
|
||||
std::cout << result.value("dir").toString() << std::endl;
|
||||
return 0;
|
||||
});
|
||||
|
||||
Parser parser(cmd);
|
||||
parser.setDisplayOptions(Parser::ShowOptionsHintFront);
|
||||
return parser.invoke(argc, argv);
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 SineStriker
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef ARGUMENT_H
|
||||
#define ARGUMENT_H
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include <syscmdline/symbol.h>
|
||||
#include <syscmdline/value.h>
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class ArgumentPrivate;
|
||||
|
||||
class SYSCMDLINE_EXPORT Argument : public Symbol {
|
||||
SYSCMDLINE_DECL_PRIVATE(Argument)
|
||||
public:
|
||||
enum Number {
|
||||
Single,
|
||||
MultiValue,
|
||||
Remainder,
|
||||
};
|
||||
using Validator = std::function<bool /* result */ (
|
||||
const std::string & /* token */, Value * /* out */, std::string * /* errorMessage */)>;
|
||||
|
||||
Argument();
|
||||
Argument(const std::string &name, const std::string &desc = {}, bool required = true,
|
||||
const Value &defaultValue = {});
|
||||
|
||||
std::string displayedText() const;
|
||||
|
||||
using Symbol::helpText;
|
||||
std::string helpText(HelpPosition pos, int displayOptions, void *extra) const override;
|
||||
|
||||
public:
|
||||
std::string name() const;
|
||||
void setName(const std::string &name);
|
||||
|
||||
std::string displayName() const;
|
||||
void setDisplayName(const std::string &displayName);
|
||||
|
||||
bool isRequired() const;
|
||||
void setRequired(bool required);
|
||||
|
||||
inline bool isOptional() const;
|
||||
inline void setOptional(bool optional);
|
||||
|
||||
Value defaultValue() const;
|
||||
void setDefaultValue(const Value &defaultValue);
|
||||
|
||||
const std::vector<Value> &expectedValues() const;
|
||||
void setExpectedValues(const std::vector<Value> &expectedValues);
|
||||
|
||||
bool multiValueEnabled() const;
|
||||
SYSCMDLINE_DECL_DEPRECATED void setMultiValueEnabled(bool on);
|
||||
|
||||
Number number() const;
|
||||
void setNumber(Number valuePolicy);
|
||||
|
||||
Validator validator() const;
|
||||
void setValidator(const Validator &validator);
|
||||
|
||||
public:
|
||||
inline Argument &metavar(const std::string &metavar);
|
||||
inline Argument &required(bool required = true);
|
||||
inline Argument &default_value(const Value &value);
|
||||
inline Argument &expect(const std::vector<Value> &expectedValues);
|
||||
SYSCMDLINE_DECL_DEPRECATED inline Argument &multi(bool multiValueEnabled = true);
|
||||
inline Argument &validate(const Validator &validator);
|
||||
inline Argument &nargs(Number valuePolicy);
|
||||
};
|
||||
|
||||
inline bool Argument::isOptional() const {
|
||||
return !isRequired();
|
||||
}
|
||||
|
||||
inline void Argument::setOptional(bool optional) {
|
||||
setRequired(!optional);
|
||||
}
|
||||
|
||||
inline Argument &Argument::metavar(const std::string &metavar) {
|
||||
setDisplayName(metavar);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Argument &Argument::required(bool required) {
|
||||
setRequired(required);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Argument &Argument::default_value(const Value &value) {
|
||||
setDefaultValue(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Argument &Argument::expect(const std::vector<Value> &expectedValues) {
|
||||
setExpectedValues(expectedValues);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Argument &Argument::multi(bool multiValueEnabled) {
|
||||
setMultiValueEnabled(multiValueEnabled);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Argument &Argument::validate(const Argument::Validator &validator) {
|
||||
setValidator(validator);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Argument &Argument::nargs(const Argument::Number valuePolicy) {
|
||||
setNumber(valuePolicy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
class ArgumentHolderPrivate;
|
||||
|
||||
class SYSCMDLINE_EXPORT ArgumentHolder : public Symbol {
|
||||
SYSCMDLINE_DECL_PRIVATE(ArgumentHolder)
|
||||
public:
|
||||
std::string displayedArguments(int displayOptions) const;
|
||||
|
||||
public:
|
||||
inline void addArgument(const Argument &argument);
|
||||
void addArguments(const std::vector<Argument> &arguments);
|
||||
|
||||
using Symbol::helpText;
|
||||
|
||||
protected:
|
||||
ArgumentHolder(ArgumentHolderPrivate *d);
|
||||
};
|
||||
|
||||
inline void ArgumentHolder::addArgument(const Argument &argument) {
|
||||
addArguments({argument});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // ARGUMENT_H
|
||||
@@ -0,0 +1,147 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 SineStriker
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef COMMAND_H
|
||||
#define COMMAND_H
|
||||
|
||||
#include <syscmdline/option.h>
|
||||
#include <syscmdline/helplayout.h>
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class ParseResult;
|
||||
|
||||
class CommandCataloguePrivate;
|
||||
|
||||
class SYSCMDLINE_EXPORT CommandCatalogue : public SharedBase {
|
||||
SYSCMDLINE_DECL_PRIVATE(CommandCatalogue)
|
||||
public:
|
||||
CommandCatalogue();
|
||||
|
||||
public:
|
||||
void addArguments(const std::string &name, const std::vector<std::string> &args);
|
||||
void addOptions(const std::string &name, const std::vector<std::string> &options);
|
||||
void addCommands(const std::string &name, const std::vector<std::string> &commands);
|
||||
};
|
||||
|
||||
class CommandPrivate;
|
||||
|
||||
class SYSCMDLINE_EXPORT Command : public ArgumentHolder {
|
||||
SYSCMDLINE_DECL_PRIVATE(Command)
|
||||
public:
|
||||
using Handler = std::function<int /* code */ (const ParseResult & /* result */)>;
|
||||
|
||||
Command();
|
||||
Command(const std::string &name, const std::string &desc = {},
|
||||
const std::vector<Argument> &args = {});
|
||||
|
||||
using Symbol::helpText;
|
||||
std::string helpText(HelpPosition pos, int displayOptions, void *extra) const override;
|
||||
|
||||
public:
|
||||
std::string name() const;
|
||||
void setName(const std::string &name);
|
||||
|
||||
inline void addOption(const Option &option, const std::string &group = {});
|
||||
void addOptions(const std::vector<Option> &options, const std::string &group = {});
|
||||
|
||||
int commandCount() const;
|
||||
Command command(int index) const;
|
||||
inline void addCommand(const Command &command);
|
||||
void addCommands(const std::vector<Command> &commands);
|
||||
|
||||
std::string detailedDescription() const;
|
||||
void setDetailedDescription(const std::string &detailedDescription);
|
||||
|
||||
Handler handler() const;
|
||||
void setHandler(const Handler &handler);
|
||||
|
||||
CommandCatalogue catalogue() const;
|
||||
void setCatalogue(const CommandCatalogue &catalogue);
|
||||
|
||||
std::string versionString() const;
|
||||
|
||||
void addVersionOption(const std::string &version,
|
||||
const std::vector<std::string> &tokens = {},
|
||||
const std::string &desc = {});
|
||||
void addHelpOption(bool showHelpIfNoArg = false, bool global = false,
|
||||
const std::vector<std::string> &tokens = {},
|
||||
const std::string &desc = {});
|
||||
|
||||
HelpLayout helpLayout() const;
|
||||
void setHelpLayout(const HelpLayout &helpLayout);
|
||||
|
||||
public:
|
||||
inline Command &detailed(const std::string &detailedDescription);
|
||||
inline Command &action(const Handler &handler);
|
||||
inline Command &catalog(const CommandCatalogue &catalogue);
|
||||
inline Command &version(const std::string &version,
|
||||
const std::vector<std::string> &tokens = {},
|
||||
const std::string &desc = {});
|
||||
inline Command &help(bool showHelpIfNoArg = false, bool global = false,
|
||||
const std::vector<std::string> &tokens = {},
|
||||
const std::string &desc = {});
|
||||
};
|
||||
|
||||
inline void Command::addCommand(const Command &command) {
|
||||
addCommands({command});
|
||||
}
|
||||
|
||||
inline void Command::addOption(const Option &option, const std::string &group) {
|
||||
addOptions({option}, group);
|
||||
}
|
||||
|
||||
inline Command &Command::detailed(const std::string &detailedDescription) {
|
||||
setDetailedDescription(detailedDescription);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Command &Command::action(const Command::Handler &handler) {
|
||||
setHandler(handler);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Command &Command::catalog(const CommandCatalogue &catalogue) {
|
||||
setCatalogue(catalogue);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Command &Command::version(const std::string &version,
|
||||
const std::vector<std::string> &tokens,
|
||||
const std::string &desc) {
|
||||
addVersionOption(version, tokens, desc);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Command &Command::help(bool showHelpIfNoArg, bool global,
|
||||
const std::vector<std::string> &tokens, const std::string &desc) {
|
||||
addHelpOption(showHelpIfNoArg, global, tokens, desc);
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // COMMAND_H
|
||||
@@ -0,0 +1,71 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 SineStriker
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef GLOBAL_H
|
||||
#define GLOBAL_H
|
||||
|
||||
// Export define
|
||||
#ifdef _WIN32
|
||||
# define SYSCMDLINE_DECL_EXPORT __declspec(dllexport)
|
||||
# define SYSCMDLINE_DECL_IMPORT __declspec(dllimport)
|
||||
#else
|
||||
# define SYSCMDLINE_DECL_EXPORT __attribute__((visibility("default")))
|
||||
# define SYSCMDLINE_DECL_IMPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#ifdef SYSCMDLINE_STATIC
|
||||
# define SYSCMDLINE_EXPORT
|
||||
#else
|
||||
# ifdef SYSCMDLINE_LIBRARY
|
||||
# define SYSCMDLINE_EXPORT SYSCMDLINE_DECL_EXPORT
|
||||
# else
|
||||
# define SYSCMDLINE_EXPORT SYSCMDLINE_DECL_IMPORT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# define SYSCMDLINE_DECL_DEPRECATED __declspec(deprecated)
|
||||
#else
|
||||
# define SYSCMDLINE_DECL_DEPRECATED __attribute__((__deprecated__))
|
||||
#endif
|
||||
|
||||
// Utils
|
||||
#define SYSCMDLINE_UNUSED(X) (void) X;
|
||||
|
||||
#define SYSCMDLINE_DECL_PRIVATE(X) \
|
||||
public: \
|
||||
const X##Private *d_func() const { \
|
||||
return reinterpret_cast<const X##Private *>(d_ptr); \
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define SYSCMDLINE_PRINTF_FORMAT(fmtpos, attrpos) \
|
||||
__attribute__((__format__(__printf__, fmtpos, attrpos)))
|
||||
#else
|
||||
# define SYSCMDLINE_PRINTF_FORMAT(fmtpos, attrpos)
|
||||
#endif
|
||||
|
||||
#endif // GLOBAL_H
|
||||
@@ -0,0 +1,109 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 SineStriker
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef HELPLAYOUT_H
|
||||
#define HELPLAYOUT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include <syscmdline/sharedbase.h>
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class Parser;
|
||||
|
||||
class HelpLayoutPrivate;
|
||||
|
||||
class SYSCMDLINE_EXPORT HelpLayout : public SharedBase {
|
||||
SYSCMDLINE_DECL_PRIVATE(HelpLayout)
|
||||
public:
|
||||
HelpLayout();
|
||||
|
||||
enum HelpTextItem {
|
||||
HT_Prologue,
|
||||
HT_Epilogue,
|
||||
HT_Description,
|
||||
HT_Usage,
|
||||
};
|
||||
|
||||
enum HelpListItem {
|
||||
HL_Arguments,
|
||||
HL_Options,
|
||||
HL_Commands,
|
||||
};
|
||||
|
||||
enum MessageItem {
|
||||
MI_Information,
|
||||
MI_Warning,
|
||||
MI_Critical,
|
||||
};
|
||||
|
||||
struct Text {
|
||||
std::string title;
|
||||
std::string lines;
|
||||
};
|
||||
|
||||
struct List {
|
||||
std::string title;
|
||||
std::vector<std::string> firstColumn;
|
||||
std::vector<std::string> secondColumn;
|
||||
};
|
||||
|
||||
struct Context {
|
||||
const Parser *parser;
|
||||
union {
|
||||
// Available on `HelpTextItem`, `UserHelpTextItem`
|
||||
const Text *text;
|
||||
|
||||
// Available on `HelpListItem`, `UserHelpListItem`
|
||||
struct {
|
||||
const List *list;
|
||||
int firstColumnLength;
|
||||
};
|
||||
};
|
||||
bool hasNext;
|
||||
};
|
||||
|
||||
using Output = std::function<void(const Context & /* context */)>;
|
||||
|
||||
public:
|
||||
void addHelpTextItem(HelpTextItem type, const Output &out = {});
|
||||
void addHelpListItem(HelpListItem type, const Output &out = {});
|
||||
void addMessageItem(MessageItem type, const Output &out = {});
|
||||
|
||||
void addUserHelpTextItem(const Text &text, const Output &out = {});
|
||||
void addUserHelpListItem(const List &list, const Output &out = {});
|
||||
void addUserIntroItem(const Output &out);
|
||||
|
||||
public:
|
||||
static HelpLayout defaultHelpLayout();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HELPLAYOUT_H
|
||||
@@ -0,0 +1,178 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 SineStriker
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef OPTION_H
|
||||
#define OPTION_H
|
||||
|
||||
#include <syscmdline/argument.h>
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class OptionPrivate;
|
||||
|
||||
class SYSCMDLINE_EXPORT Option : public ArgumentHolder {
|
||||
SYSCMDLINE_DECL_PRIVATE(Option)
|
||||
public:
|
||||
enum Role {
|
||||
NoRole,
|
||||
Debug,
|
||||
Verbose,
|
||||
Version,
|
||||
Help,
|
||||
};
|
||||
|
||||
enum PriorLevel {
|
||||
NoPrior,
|
||||
IgnoreMissingArguments,
|
||||
IgnoreMissingSymbols,
|
||||
AutoSetWhenNoSymbols,
|
||||
ExclusiveToArguments,
|
||||
ExclusiveToOptions,
|
||||
ExclusiveToAll,
|
||||
};
|
||||
|
||||
enum ShortMatchRule {
|
||||
NoShortMatch,
|
||||
ShortMatchSingleLetter,
|
||||
ShortMatchSingleChar,
|
||||
ShortMatchAll,
|
||||
};
|
||||
|
||||
Option();
|
||||
Option(Role role, const std::vector<std::string> &tokens = {}, const std::string &desc = {});
|
||||
Option(const std::string &token, const std::string &desc = {}, const Argument &arg = {},
|
||||
bool required = false);
|
||||
Option(const std::vector<std::string> &tokens, const std::string &desc = {},
|
||||
const Argument &arg = {}, bool required = false);
|
||||
inline Option(std::initializer_list<std::string> tokens, const std::string &desc = {},
|
||||
const Argument &arg = {}, bool required = false);
|
||||
|
||||
using Symbol::helpText;
|
||||
std::string helpText(HelpPosition pos, int displayOptions, void *extra) const override;
|
||||
|
||||
public:
|
||||
Role role() const;
|
||||
void setRole(Role role);
|
||||
|
||||
const std::vector<std::string> &tokens() const;
|
||||
inline std::string token() const;
|
||||
void setTokens(const std::vector<std::string> &tokens);
|
||||
inline void setToken(const std::string &token);
|
||||
|
||||
bool isRequired() const;
|
||||
void setRequired(bool required);
|
||||
|
||||
inline bool isOptional() const;
|
||||
inline void setOptional(bool optional);
|
||||
|
||||
ShortMatchRule shortMatchRule() const;
|
||||
void setShortMatchRule(ShortMatchRule shortMatchRule);
|
||||
|
||||
PriorLevel priorLevel() const;
|
||||
void setPriorLevel(PriorLevel priorLevel);
|
||||
|
||||
bool isGlobal() const;
|
||||
void setGlobal(bool on);
|
||||
|
||||
int maxOccurrence() const;
|
||||
void setMaxOccurrence(int max);
|
||||
inline void setUnlimitedOccurrence();
|
||||
|
||||
public:
|
||||
inline Option &arg(const std::string &name, bool required = true,
|
||||
const Value &defaultValue = {});
|
||||
inline Option &arg(const Argument &arg);
|
||||
inline Option &required(bool required = true);
|
||||
inline Option &short_match(ShortMatchRule shortMatchRule = ShortMatchAll);
|
||||
inline Option &prior(PriorLevel priorLevel);
|
||||
inline Option &global(bool global = true);
|
||||
inline Option &multi(int maxOccurrence = 0);
|
||||
};
|
||||
|
||||
inline Option::Option(std::initializer_list<std::string> tokens, const std::string &desc,
|
||||
const Argument &arg, bool required)
|
||||
: Option(std::vector<std::string>(tokens), desc, arg, required) {
|
||||
}
|
||||
|
||||
inline std::string Option::token() const {
|
||||
return tokens().front();
|
||||
}
|
||||
|
||||
inline void Option::setToken(const std::string &token) {
|
||||
setTokens({token});
|
||||
}
|
||||
|
||||
inline bool Option::isOptional() const {
|
||||
return !isRequired();
|
||||
}
|
||||
|
||||
inline void Option::setOptional(bool optional) {
|
||||
setRequired(!optional);
|
||||
}
|
||||
|
||||
inline void Option::setUnlimitedOccurrence() {
|
||||
setMaxOccurrence(0);
|
||||
}
|
||||
|
||||
inline Option &Option::arg(const std::string &name, bool required, const Value &defaultValue) {
|
||||
// Option's argument doesn't need a description
|
||||
addArgument(Argument(name, {}, required, defaultValue));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Option &Option::arg(const Argument &arg) {
|
||||
addArgument(arg);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Option &Option::required(bool required) {
|
||||
setRequired(required);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Option &Option::short_match(Option::ShortMatchRule shortMatchRule) {
|
||||
setShortMatchRule(shortMatchRule);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Option &Option::prior(Option::PriorLevel priorLevel) {
|
||||
setPriorLevel(priorLevel);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Option &Option::global(bool global) {
|
||||
setGlobal(global);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Option &Option::multi(int maxOccurrence) {
|
||||
setMaxOccurrence(maxOccurrence);
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // OPTION_H
|
||||
@@ -0,0 +1,121 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 SineStriker
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef PARSER_H
|
||||
#define PARSER_H
|
||||
|
||||
#include <syscmdline/parseresult.h>
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class ParserPrivate;
|
||||
|
||||
class SYSCMDLINE_EXPORT Parser : public SharedBase {
|
||||
SYSCMDLINE_DECL_PRIVATE(Parser)
|
||||
public:
|
||||
Parser();
|
||||
Parser(const Command &rootCommand);
|
||||
|
||||
public:
|
||||
enum ParseOption {
|
||||
Standard = 0,
|
||||
IgnoreCommandCase = 0x1,
|
||||
IgnoreOptionCase = 0x2,
|
||||
AllowUnixGroupFlags = 0x4,
|
||||
AllowDosShortOptions = 0x8,
|
||||
DontAllowUnixShortOptions = 0x10,
|
||||
EnableResponseFile = 0x20,
|
||||
};
|
||||
|
||||
enum DisplayOption {
|
||||
Normal = 0,
|
||||
DontShowHelpOnError = 0x1,
|
||||
DontShowIntroOnError = 0x2,
|
||||
SkipCorrection = 0x4,
|
||||
ShowArgumentDefaultValue = 0x8,
|
||||
ShowArgumentExpectedValues = 0x10,
|
||||
ShowArgumentIsRequired = 0x20,
|
||||
ShowOptionIsRequired = 0x40,
|
||||
AlignAllCatalogues = 0x80,
|
||||
DontShowRequiredOptionsOnUsage = 0x100,
|
||||
ShowOptionalOptionsOnUsage = 0x200,
|
||||
ShowOptionsBehindArguments = 0x400,
|
||||
ShowOptionsHintFront = 0x800,
|
||||
};
|
||||
|
||||
std::string prologue() const;
|
||||
void setPrologue(const std::string &prologue);
|
||||
|
||||
std::string epilogue() const;
|
||||
void setEpilogue(const std::string &epilogue);
|
||||
|
||||
int displayOptions() const;
|
||||
void setDisplayOptions(int displayOptions);
|
||||
|
||||
Command rootCommand() const;
|
||||
void setRootCommand(const Command &rootCommand);
|
||||
|
||||
ParseResult parse(const std::vector<std::string> &args, int parseOptions = Standard);
|
||||
inline int invoke(const std::vector<std::string> &args, int errCode = -1,
|
||||
int parseOptions = Standard);
|
||||
|
||||
// Don't use this pair of API on Windows because the arguemnts passed by `main`
|
||||
// entry is in ANSI encoding, but the library uses UTF-8.
|
||||
inline ParseResult parse(int argc, char **argv, int parseOptions = Standard);
|
||||
inline int invoke(int argc, char **argv, int errCode = -1, int parseOptions = Standard);
|
||||
|
||||
public:
|
||||
using TextProvider = std::string (*)(int /* category */, int /* index */);
|
||||
|
||||
enum SizeType {
|
||||
ST_Indent,
|
||||
ST_Spacing,
|
||||
ST_ConsoleWidth,
|
||||
};
|
||||
int size(SizeType sizeType) const;
|
||||
void setSize(SizeType sizeType, int value);
|
||||
|
||||
TextProvider textProvider();
|
||||
void setTextProvider(TextProvider textProvider);
|
||||
|
||||
static TextProvider defaultTextProvider();
|
||||
};
|
||||
|
||||
inline int Parser::invoke(const std::vector<std::string> &args, int errCode, int parseOptions) {
|
||||
return parse(args, parseOptions).invoke(errCode);
|
||||
}
|
||||
|
||||
inline ParseResult Parser::parse(int argc, char **argv, int parseOptions) {
|
||||
return parse({argv, argv + argc}, parseOptions);
|
||||
}
|
||||
|
||||
inline int Parser::invoke(int argc, char **argv, int errCode, int parseOptions) {
|
||||
return parse({argv, argv + argc}, parseOptions).invoke(errCode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // PARSER_H
|
||||
@@ -0,0 +1,279 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 SineStriker
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef PARSERESULT_H
|
||||
#define PARSERESULT_H
|
||||
|
||||
#include <syscmdline/command.h>
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class Parser;
|
||||
|
||||
class ParseResultPrivate;
|
||||
|
||||
class SYSCMDLINE_EXPORT OptionResult {
|
||||
public:
|
||||
OptionResult();
|
||||
|
||||
public:
|
||||
Option option() const;
|
||||
int indexOf(const std::string &name) const;
|
||||
int count() const;
|
||||
inline bool isSet() const;
|
||||
|
||||
inline bool isArgumentSet(const Argument &arg, int n = 0) const;
|
||||
inline bool isArgumentSet(const std::string &name, int n = 0) const;
|
||||
inline bool isArgumentSet(int index, int n = 0) const;
|
||||
|
||||
// Get all values of an argument
|
||||
inline std::vector<Value> allValues(const Argument &arg) const;
|
||||
inline std::vector<Value> allValues(const std::string &name) const;
|
||||
std::vector<Value> allValues(int index = 0) const;
|
||||
|
||||
// Get values of multi-value argument at the option's N-th occurrence
|
||||
inline const std::vector<Value> &values(const Argument &arg, int n = 0) const;
|
||||
inline const std::vector<Value> &values(const std::string &name, int n = 0) const;
|
||||
const std::vector<Value> &values(int index = 0, int n = 0) const;
|
||||
|
||||
// Get value of single-value argument at the option's N-th occurrence or its default value
|
||||
inline Value value(const Argument &arg, int n = 0) const;
|
||||
inline Value value(const std::string &name, int n = 0) const;
|
||||
Value value(int index = 0, int n = 0) const;
|
||||
|
||||
private:
|
||||
inline OptionResult(const void *data);
|
||||
const void *data;
|
||||
|
||||
friend class ParseResult;
|
||||
};
|
||||
|
||||
inline bool OptionResult::isSet() const {
|
||||
return count() > 0;
|
||||
}
|
||||
|
||||
inline bool OptionResult::isArgumentSet(const Argument &arg, int n) const {
|
||||
return !values(indexOf(arg.name()), n).empty();
|
||||
}
|
||||
|
||||
inline bool OptionResult::isArgumentSet(const std::string &name, int n) const {
|
||||
return !values(indexOf(name), n).empty();
|
||||
}
|
||||
|
||||
inline bool OptionResult::isArgumentSet(int index, int n) const {
|
||||
return !values(index, n).empty();
|
||||
}
|
||||
|
||||
inline std::vector<Value> OptionResult::allValues(const Argument &arg) const {
|
||||
return allValues(indexOf(arg.name()));
|
||||
}
|
||||
|
||||
inline std::vector<Value> OptionResult::allValues(const std::string &name) const {
|
||||
return allValues(indexOf(name));
|
||||
}
|
||||
|
||||
inline const std::vector<Value> &OptionResult::values(const Argument &arg, int n) const {
|
||||
return values(indexOf(arg.name()), n);
|
||||
}
|
||||
|
||||
inline const std::vector<Value> &OptionResult::values(const std::string &name, int n) const {
|
||||
return values(indexOf(name), n);
|
||||
}
|
||||
|
||||
inline Value OptionResult::value(const Argument &arg, int n) const {
|
||||
return value(indexOf(arg.name()), n);
|
||||
}
|
||||
|
||||
inline Value OptionResult::value(const std::string &name, int n) const {
|
||||
return value(indexOf(name), n);
|
||||
}
|
||||
|
||||
inline OptionResult::OptionResult(const void *data) : data(data) {
|
||||
}
|
||||
|
||||
class SYSCMDLINE_EXPORT ParseResult : public SharedBase {
|
||||
SYSCMDLINE_DECL_PRIVATE(ParseResult)
|
||||
public:
|
||||
ParseResult();
|
||||
|
||||
inline bool isValid() const;
|
||||
|
||||
public:
|
||||
Command rootCommand() const;
|
||||
const std::vector<std::string> &arguments() const;
|
||||
|
||||
int invoke(int errCode = -1) const;
|
||||
int dispatch() const;
|
||||
|
||||
public:
|
||||
enum Error {
|
||||
NoError,
|
||||
UnknownOption,
|
||||
UnknownCommand,
|
||||
MissingOptionArgument,
|
||||
MissingCommandArgument,
|
||||
TooManyArguments,
|
||||
InvalidArgumentValue,
|
||||
InvalidOptionPosition,
|
||||
MissingRequiredOption,
|
||||
OptionOccurTooMuch,
|
||||
ArgumentTypeMismatch,
|
||||
ArgumentValidateFailed,
|
||||
MutuallyExclusiveOptions,
|
||||
PriorOptionWithArguments,
|
||||
PriorOptionWithOptions,
|
||||
ErrorReadingResponseFile,
|
||||
};
|
||||
|
||||
Error error() const;
|
||||
std::string errorText() const;
|
||||
std::string correctionText() const;
|
||||
std::string cancellationToken() const;
|
||||
|
||||
Command command() const;
|
||||
std::vector<Option> globalOptions() const;
|
||||
std::vector<int> commandIndexStack() const;
|
||||
int indexOfArgument(const std::string &name) const;
|
||||
int indexOfOption(const std::string &token) const;
|
||||
|
||||
void showError() const;
|
||||
void showHelpText() const;
|
||||
void showMessage(const std::string &info, const std::string &warning,
|
||||
const std::string &error) const;
|
||||
|
||||
public:
|
||||
inline bool isVersionSet() const;
|
||||
inline bool isHelpSet() const;
|
||||
bool isRoleSet(Option::Role role) const;
|
||||
|
||||
inline bool isArgumentSet(const Argument &arg) const;
|
||||
inline bool isArgumentSet(const std::string &name) const;
|
||||
inline bool isArgumentSet(int index) const;
|
||||
|
||||
// Get values of multi-value argument
|
||||
inline const std::vector<Value> &values(const Argument &arg) const;
|
||||
inline const std::vector<Value> &values(const std::string &name) const;
|
||||
const std::vector<Value> &values(int index) const;
|
||||
|
||||
// Get value of single-value argument or its default value
|
||||
inline Value value(const Argument &arg) const;
|
||||
inline Value value(const std::string &name) const;
|
||||
Value value(int index) const;
|
||||
|
||||
inline bool isOptionSet(const Option &option) const;
|
||||
inline bool isOptionSet(const std::string &token) const;
|
||||
inline bool isOptionSet(int index) const;
|
||||
|
||||
// Detailed result for an option
|
||||
inline OptionResult option(const Option &option) const;
|
||||
inline OptionResult option(const std::string &token) const;
|
||||
OptionResult option(int index) const;
|
||||
|
||||
// Get value of single-value argument of given option or its default value
|
||||
inline Value valueForOption(const Option &option) const;
|
||||
inline Value valueForOption(const std::string &token) const;
|
||||
inline Value valueForOption(int index) const;
|
||||
|
||||
protected:
|
||||
ParseResult(ParseResultPrivate *d);
|
||||
friend class Parser;
|
||||
};
|
||||
|
||||
inline bool ParseResult::isValid() const {
|
||||
return d_ptr != nullptr;
|
||||
}
|
||||
|
||||
inline bool ParseResult::isVersionSet() const {
|
||||
return isRoleSet(Option::Version);
|
||||
}
|
||||
|
||||
inline bool ParseResult::isHelpSet() const {
|
||||
return isRoleSet(Option::Help);
|
||||
}
|
||||
|
||||
inline bool ParseResult::isArgumentSet(const Argument &arg) const {
|
||||
return isArgumentSet(indexOfArgument(arg.name()));
|
||||
}
|
||||
|
||||
inline bool ParseResult::isArgumentSet(const std::string &name) const {
|
||||
return isArgumentSet(indexOfArgument(name));
|
||||
}
|
||||
|
||||
inline bool ParseResult::isArgumentSet(int index) const {
|
||||
return !values(index).empty();
|
||||
}
|
||||
|
||||
inline const std::vector<Value> &ParseResult::values(const Argument &arg) const {
|
||||
return values(indexOfArgument(arg.name()));
|
||||
}
|
||||
|
||||
inline const std::vector<Value> &ParseResult::values(const std::string &name) const {
|
||||
return values(indexOfArgument(name));
|
||||
}
|
||||
|
||||
inline Value ParseResult::value(const Argument &arg) const {
|
||||
return value(indexOfArgument(arg.name()));
|
||||
}
|
||||
|
||||
inline Value ParseResult::value(const std::string &name) const {
|
||||
return value(indexOfArgument(name));
|
||||
}
|
||||
|
||||
inline bool ParseResult::isOptionSet(const Option &option) const {
|
||||
return this->option(indexOfOption(option.token())).count() > 0;
|
||||
}
|
||||
|
||||
inline bool ParseResult::isOptionSet(const std::string &token) const {
|
||||
return option(indexOfOption(token)).count() > 0;
|
||||
}
|
||||
|
||||
inline bool ParseResult::isOptionSet(int index) const {
|
||||
return option(index).count() > 0;
|
||||
}
|
||||
|
||||
inline OptionResult ParseResult::option(const Option &option) const {
|
||||
return this->option(indexOfOption(option.token()));
|
||||
}
|
||||
|
||||
inline OptionResult ParseResult::option(const std::string &token) const {
|
||||
return option(indexOfOption(token));
|
||||
}
|
||||
|
||||
inline Value ParseResult::valueForOption(const Option &option) const {
|
||||
return this->option(indexOfOption(option.token())).value();
|
||||
}
|
||||
|
||||
inline Value ParseResult::valueForOption(const std::string &token) const {
|
||||
return option(indexOfOption(token)).value();
|
||||
}
|
||||
|
||||
inline Value ParseResult::valueForOption(int index) const {
|
||||
return option(index).value();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // PARSERESULT_H
|
||||
@@ -0,0 +1,72 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 SineStriker
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef SHAREDBASE_H
|
||||
#define SHAREDBASE_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <syscmdline/global.h>
|
||||
namespace SysCmdLine {
|
||||
|
||||
class SharedBasePrivate;
|
||||
|
||||
class SYSCMDLINE_EXPORT SharedBase {
|
||||
SYSCMDLINE_DECL_PRIVATE(SharedBase)
|
||||
public:
|
||||
virtual ~SharedBase();
|
||||
|
||||
SharedBase(const SharedBase &other);
|
||||
inline SharedBase(SharedBase &&other) noexcept;
|
||||
SharedBase &operator=(const SharedBase &other);
|
||||
inline SharedBase &operator=(SharedBase &&other) noexcept;
|
||||
|
||||
public:
|
||||
inline void swap(SharedBase &other) noexcept;
|
||||
bool isDetached() const;
|
||||
void detach();
|
||||
|
||||
protected:
|
||||
SharedBase(SharedBasePrivate *d);
|
||||
SharedBasePrivate *d_ptr;
|
||||
};
|
||||
|
||||
inline SharedBase::SharedBase(SharedBase &&other) noexcept : d_ptr(nullptr) {
|
||||
swap(other);
|
||||
}
|
||||
|
||||
inline SharedBase &SharedBase::operator=(SharedBase &&other) noexcept {
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void SharedBase::swap(SharedBase &other) noexcept {
|
||||
std::swap(d_ptr, other.d_ptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // SHAREDBASE_H
|
||||
@@ -0,0 +1,75 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 SineStriker
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef STRINGS_H
|
||||
#define STRINGS_H
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
namespace Strings {
|
||||
|
||||
enum TextCategory {
|
||||
ParseError,
|
||||
Title,
|
||||
OptionRole,
|
||||
Information,
|
||||
Token,
|
||||
};
|
||||
|
||||
enum TitleText {
|
||||
Error,
|
||||
Usage,
|
||||
Description,
|
||||
Arguments,
|
||||
Options,
|
||||
Commands,
|
||||
Required,
|
||||
Default,
|
||||
ExpectedValues,
|
||||
};
|
||||
|
||||
enum OptionRoleText {
|
||||
NoRole,
|
||||
Debug,
|
||||
Verbose,
|
||||
Version,
|
||||
Help,
|
||||
};
|
||||
|
||||
enum InformationText {
|
||||
MatchCommand,
|
||||
};
|
||||
|
||||
enum TokenText {
|
||||
OptionalCommands,
|
||||
OptionalOptions,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // STRINGS_H
|
||||
@@ -0,0 +1,81 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 SineStriker
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef SYMBOL_H
|
||||
#define SYMBOL_H
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include <syscmdline/sharedbase.h>
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class SymbolPrivate;
|
||||
|
||||
class SYSCMDLINE_EXPORT Symbol : public SharedBase {
|
||||
SYSCMDLINE_DECL_PRIVATE(Symbol)
|
||||
public:
|
||||
enum SymbolType {
|
||||
ST_Command,
|
||||
ST_Option,
|
||||
ST_Argument,
|
||||
};
|
||||
|
||||
enum HelpPosition {
|
||||
HP_Usage,
|
||||
HP_ErrorText,
|
||||
HP_FirstColumn,
|
||||
HP_SecondColumn,
|
||||
};
|
||||
|
||||
using HelpProvider =
|
||||
std::function<std::string(const Symbol * /* this */, HelpPosition /* pos */,
|
||||
int /* displayOptions */, void * /* extra */)>;
|
||||
|
||||
inline std::string helpText(HelpPosition pos, int displayOptions) const;
|
||||
virtual std::string helpText(HelpPosition pos, int displayOptions, void *extra) const;
|
||||
|
||||
public:
|
||||
SymbolType type() const;
|
||||
|
||||
std::string description() const;
|
||||
void setDescription(const std::string &desc);
|
||||
|
||||
HelpProvider helpProvider() const;
|
||||
void setHelpProvider(const HelpProvider &helpProvider);
|
||||
|
||||
protected:
|
||||
Symbol(SymbolPrivate *d);
|
||||
};
|
||||
|
||||
inline std::string Symbol::helpText(Symbol::HelpPosition pos, int displayOptions) const {
|
||||
return helpText(pos, displayOptions, nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // SYMBOL_H
|
||||
@@ -0,0 +1,83 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 SineStriker
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef SYSTEM_H
|
||||
#define SYSTEM_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <syscmdline/global.h>
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
SYSCMDLINE_EXPORT std::string wideToUtf8(const std::wstring &s);
|
||||
SYSCMDLINE_EXPORT std::wstring utf8ToWide(const std::string &s);
|
||||
|
||||
#ifdef _WIN32
|
||||
SYSCMDLINE_EXPORT std::string ansiToUtf8(const std::string &s);
|
||||
#endif
|
||||
|
||||
SYSCMDLINE_EXPORT std::string appPath();
|
||||
SYSCMDLINE_EXPORT std::string appDirectory();
|
||||
SYSCMDLINE_EXPORT std::string appFileName();
|
||||
SYSCMDLINE_EXPORT std::string appName();
|
||||
SYSCMDLINE_EXPORT std::vector<std::string> commandLineArguments();
|
||||
|
||||
enum ConsoleColor {
|
||||
DefaultColor = -1,
|
||||
Black = 0x0,
|
||||
Red = 0x1,
|
||||
Green = 0x2,
|
||||
Blue = 0x4,
|
||||
Yellow = Red | Green,
|
||||
Purple = Red | Blue,
|
||||
Cyan = Green | Blue,
|
||||
White = Red | Green | Blue,
|
||||
Intensified = 0x100,
|
||||
};
|
||||
|
||||
int u8printf(int foreground, int background, const char *fmt, ...)
|
||||
SYSCMDLINE_PRINTF_FORMAT(3, 4);
|
||||
|
||||
int u8vprintf(int foreground, int background, const char *fmt, va_list args);
|
||||
|
||||
enum MessageType {
|
||||
MT_Debug,
|
||||
MT_Message,
|
||||
MT_Healthy,
|
||||
MT_Warning,
|
||||
MT_Critical,
|
||||
};
|
||||
|
||||
SYSCMDLINE_EXPORT int u8debug(MessageType messageType, bool highlight, const char *fmt, ...)
|
||||
SYSCMDLINE_PRINTF_FORMAT(3, 4);
|
||||
|
||||
SYSCMDLINE_EXPORT int u8info(const char *fmt, ...) SYSCMDLINE_PRINTF_FORMAT(1, 2);
|
||||
|
||||
}
|
||||
|
||||
#endif // SYSTEM_H
|
||||
@@ -0,0 +1,120 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 SineStriker
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef VALUE_H
|
||||
#define VALUE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#include <syscmdline/global.h>
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class SYSCMDLINE_EXPORT Value {
|
||||
public:
|
||||
enum Type {
|
||||
Null,
|
||||
Bool,
|
||||
Int,
|
||||
Int64,
|
||||
Double,
|
||||
String,
|
||||
};
|
||||
|
||||
Value(Type type = Null);
|
||||
inline Value(bool b);
|
||||
inline Value(int i);
|
||||
inline Value(int64_t l);
|
||||
inline Value(double d);
|
||||
inline Value(const std::string &s);
|
||||
inline Value(const char *ch, int size = -1);
|
||||
~Value();
|
||||
|
||||
Value(const Value &other);
|
||||
Value(Value &&other) noexcept;
|
||||
Value &operator=(const Value &other);
|
||||
Value &operator=(Value &&other) noexcept;
|
||||
|
||||
inline Type type() const;
|
||||
bool isEmpty() const;
|
||||
|
||||
bool toBool() const;
|
||||
int toInt() const;
|
||||
int64_t toInt64() const;
|
||||
double toDouble() const;
|
||||
std::string toString() const;
|
||||
|
||||
bool operator==(const Value &other) const;
|
||||
bool operator!=(const Value &other) const;
|
||||
|
||||
static Value fromString(const std::string &s, Type type);
|
||||
static std::vector<std::string> toStringList(const std::vector<Value> &values);
|
||||
static const char *typeName(Type type);
|
||||
|
||||
protected:
|
||||
union {
|
||||
bool b;
|
||||
int i;
|
||||
int64_t l;
|
||||
double d;
|
||||
std::string *s;
|
||||
} data;
|
||||
Type _type;
|
||||
};
|
||||
|
||||
inline Value::Value(bool b) : _type(Bool) {
|
||||
data.b = b;
|
||||
}
|
||||
|
||||
inline Value::Value(int i) : _type(Int) {
|
||||
data.i = i;
|
||||
}
|
||||
|
||||
inline Value::Value(int64_t l) : _type(Int64) {
|
||||
data.l = l;
|
||||
}
|
||||
|
||||
inline Value::Value(double d) : _type(Double) {
|
||||
data.d = d;
|
||||
}
|
||||
|
||||
inline Value::Value(const std::string &s) : _type(String) {
|
||||
data.s = new std::string(s);
|
||||
}
|
||||
|
||||
inline Value::Value(const char *ch, int size) : _type(String) {
|
||||
data.s = size >= 0 ? new std::string(ch, size) : new std::string(ch);
|
||||
}
|
||||
|
||||
inline Value::Type Value::type() const {
|
||||
return _type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // VALUE_H
|
||||
312
libs/qwindowkit/qmsetup/src/syscmdline/src/argument.cpp
Normal file
312
libs/qwindowkit/qmsetup/src/syscmdline/src/argument.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
#include "argument.h"
|
||||
#include "argument_p.h"
|
||||
|
||||
#ifdef SYSCMDLINE_ENABLE_VALIDITY_CHECK
|
||||
# include <stdexcept>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "strings.h"
|
||||
#include "parser.h"
|
||||
|
||||
#include "utils_p.h"
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
ArgumentPrivate::ArgumentPrivate(std::string name, const std::string &desc, bool required,
|
||||
Value defaultValue)
|
||||
: SymbolPrivate(Symbol::ST_Argument, desc), name(std::move(name)), required(required),
|
||||
defaultValue(std::move(defaultValue)), number(Argument::Single) {
|
||||
}
|
||||
|
||||
SymbolPrivate *ArgumentPrivate::clone() const {
|
||||
return new ArgumentPrivate(*this);
|
||||
}
|
||||
|
||||
Argument::Argument() : Argument(std::string()) {
|
||||
}
|
||||
|
||||
Argument::Argument(const std::string &name, const std::string &desc, bool required,
|
||||
const Value &defaultValue)
|
||||
: Symbol(new ArgumentPrivate(name, desc, required, defaultValue)) {
|
||||
}
|
||||
|
||||
std::string Argument::displayedText() const {
|
||||
Q_D2(Argument);
|
||||
std::string res = d->displayName.empty() ? ("<" + d->name + ">") : d->displayName;
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string Argument::helpText(Symbol::HelpPosition pos, int displayOptions,
|
||||
void *extra) const {
|
||||
Q_D2(Argument);
|
||||
if (auto ss = Symbol::helpText(pos, displayOptions, extra); !ss.empty()) {
|
||||
return ss;
|
||||
}
|
||||
|
||||
switch (pos) {
|
||||
case Symbol::HP_Usage: {
|
||||
return displayedText() + ((d->number != Single) ? "..." : "");
|
||||
}
|
||||
case Symbol::HP_SecondColumn: {
|
||||
auto textProvider = reinterpret_cast<Parser::TextProvider>(extra);
|
||||
if (!textProvider) {
|
||||
textProvider = Parser::defaultTextProvider();
|
||||
}
|
||||
|
||||
std::string appendix;
|
||||
// Required
|
||||
if (d->required && (displayOptions & Parser::ShowArgumentIsRequired)) {
|
||||
appendix += " [" + textProvider(Strings::Title, Strings::Required) + "]";
|
||||
}
|
||||
|
||||
// Default Value
|
||||
if (d->defaultValue.type() != Value::Null &&
|
||||
(displayOptions & Parser::ShowArgumentDefaultValue)) {
|
||||
appendix += " [" + textProvider(Strings::Title, Strings::Default) + ": " +
|
||||
d->defaultValue.toString() + "]";
|
||||
}
|
||||
|
||||
// Expected Values
|
||||
if (!d->expectedValues.empty() &&
|
||||
(displayOptions & Parser::ShowArgumentExpectedValues)) {
|
||||
std::vector<std::string> values;
|
||||
values.reserve(d->expectedValues.size());
|
||||
for (const auto &item : d->expectedValues) {
|
||||
switch (item.type()) {
|
||||
case Value::String:
|
||||
values.push_back("\"" + item.toString() + "\"");
|
||||
break;
|
||||
default:
|
||||
values.push_back(item.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
appendix += " [" + textProvider(Strings::Title, Strings::ExpectedValues) +
|
||||
": " + Utils::join(values, ", ") + "]";
|
||||
}
|
||||
return d->desc + appendix;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return displayedText();
|
||||
}
|
||||
|
||||
std::string Argument::name() const {
|
||||
Q_D2(Argument);
|
||||
return d->name;
|
||||
}
|
||||
|
||||
void Argument::setName(const std::string &name) {
|
||||
Q_D(Argument);
|
||||
d->name = name;
|
||||
}
|
||||
|
||||
std::string Argument::displayName() const {
|
||||
Q_D2(Argument);
|
||||
return d->displayName;
|
||||
}
|
||||
|
||||
void Argument::setDisplayName(const std::string &displayName) {
|
||||
Q_D(Argument);
|
||||
d->displayName = displayName;
|
||||
}
|
||||
|
||||
bool Argument::isRequired() const {
|
||||
Q_D2(Argument);
|
||||
return d->required;
|
||||
}
|
||||
|
||||
void Argument::setRequired(bool required) {
|
||||
Q_D(Argument);
|
||||
d->required = required;
|
||||
}
|
||||
|
||||
Value Argument::defaultValue() const {
|
||||
Q_D2(Argument);
|
||||
return d->defaultValue;
|
||||
}
|
||||
|
||||
void Argument::setDefaultValue(const Value &defaultValue) {
|
||||
Q_D(Argument);
|
||||
d->defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
const std::vector<Value> &Argument::expectedValues() const {
|
||||
Q_D2(Argument);
|
||||
return d->expectedValues;
|
||||
}
|
||||
|
||||
void Argument::setExpectedValues(const std::vector<Value> &expectedValues) {
|
||||
Q_D(Argument);
|
||||
d->expectedValues = expectedValues;
|
||||
}
|
||||
|
||||
bool Argument::multiValueEnabled() const {
|
||||
Q_D2(Argument);
|
||||
return d->number != Single;
|
||||
}
|
||||
|
||||
void Argument::setMultiValueEnabled(bool on) {
|
||||
Q_D(Argument);
|
||||
d->number = on ? MultiValue : Single;
|
||||
}
|
||||
|
||||
Argument::Number Argument::number() const {
|
||||
Q_D2(Argument);
|
||||
return d->number;
|
||||
}
|
||||
|
||||
void Argument::setNumber(Number number) {
|
||||
Q_D(Argument);
|
||||
d->number = number;
|
||||
}
|
||||
|
||||
Argument::Validator Argument::validator() const {
|
||||
Q_D2(Argument);
|
||||
return d->validator;
|
||||
}
|
||||
|
||||
void Argument::setValidator(const Validator &validator) {
|
||||
Q_D(Argument);
|
||||
d->validator = validator;
|
||||
}
|
||||
|
||||
ArgumentHolderPrivate::ArgumentHolderPrivate(Symbol::SymbolType type, const std::string &desc)
|
||||
: SymbolPrivate(type, desc) {
|
||||
}
|
||||
|
||||
#ifdef SYSCMDLINE_ENABLE_VALIDITY_CHECK
|
||||
void ArgumentHolderPrivate::checkAddedArgument(const Argument &arg) const {
|
||||
const auto &d = arg.d_func();
|
||||
const auto &name = d->name;
|
||||
|
||||
// Empty argument name?
|
||||
if (name.empty()) {
|
||||
throw std::runtime_error("argument doesn't have a name");
|
||||
}
|
||||
|
||||
// Duplicated argument name?
|
||||
if (std::any_of(arguments.begin(), arguments.end(), [&name](const Argument &arg) {
|
||||
return arg.name() == name; //
|
||||
})) {
|
||||
throw std::runtime_error(Utils::formatText("argument name \"%1\" duplicated", {name}));
|
||||
}
|
||||
|
||||
// Required argument behind optional one?
|
||||
if (!arguments.empty() && arguments.back().isOptional() && d->required) {
|
||||
throw std::runtime_error("required argument after required arguments is prohibited");
|
||||
}
|
||||
|
||||
bool hasMultiValueArgument =
|
||||
std::any_of(arguments.begin(), arguments.end(), [&name](const Argument &arg) {
|
||||
return arg.multiValueEnabled(); //
|
||||
});
|
||||
|
||||
if (arg.multiValueEnabled()) {
|
||||
// Multiple multi-value argument?
|
||||
if (hasMultiValueArgument) {
|
||||
throw std::runtime_error("at most one multi-value argument");
|
||||
}
|
||||
hasMultiValueArgument = true;
|
||||
} else if (hasMultiValueArgument && !d->required) {
|
||||
// Optional argument after multi-value argument?
|
||||
throw std::runtime_error("required argument after multi-value argument is prohibited");
|
||||
}
|
||||
|
||||
{
|
||||
const auto &expectedValues = d->expectedValues;
|
||||
const auto &defaultValue = d->defaultValue;
|
||||
if (!expectedValues.empty()) {
|
||||
// Null expected value?
|
||||
for (size_t i = 0; i < expectedValues.size(); ++i) {
|
||||
if (expectedValues[i].type() == Value::Null) {
|
||||
throw std::runtime_error(
|
||||
Utils::formatText("expected value at %1 is null", {std::to_string(i)}));
|
||||
}
|
||||
}
|
||||
|
||||
// Invalid default value?
|
||||
if (defaultValue.type() != Value::Null &&
|
||||
std::find(expectedValues.begin(), expectedValues.end(), defaultValue) ==
|
||||
expectedValues.end()) {
|
||||
throw std::runtime_error(Utils::formatText(
|
||||
"default value \"%1\" is not in expect values", {defaultValue.toString()}));
|
||||
}
|
||||
}
|
||||
|
||||
const auto &validator = d->validator;
|
||||
if (validator && defaultValue.type() != Value::Null) {
|
||||
// Validator is incompatible with the default value?
|
||||
Value val;
|
||||
std::string errorMessage;
|
||||
auto res = validator(defaultValue.toString(), &val, &errorMessage);
|
||||
if (!res) {
|
||||
throw std::runtime_error("validator is not able to handle the default value.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string ArgumentHolder::displayedArguments(int displayOptions) const {
|
||||
Q_D2(ArgumentHolder);
|
||||
|
||||
// Use C Style to traverse
|
||||
auto arguments = d->arguments.data();
|
||||
int size = int(d->arguments.size());
|
||||
|
||||
std::string ss;
|
||||
int optionalIdx = size;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (!arguments[i].isRequired()) {
|
||||
optionalIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optionalIdx > 0) {
|
||||
for (int i = 0; i < optionalIdx - 1; ++i) {
|
||||
ss += arguments[i].helpText(Symbol::HP_Usage, displayOptions, nullptr);
|
||||
ss += " ";
|
||||
}
|
||||
ss += arguments[optionalIdx - 1].helpText(Symbol::HP_Usage, displayOptions, nullptr);
|
||||
}
|
||||
|
||||
if (optionalIdx < size) {
|
||||
if (optionalIdx > 0)
|
||||
ss += " ";
|
||||
ss += "[";
|
||||
for (int i = optionalIdx; i < size - 1; ++i) {
|
||||
ss += arguments[i].helpText(Symbol::HP_Usage, displayOptions, nullptr);
|
||||
ss += " ";
|
||||
}
|
||||
ss += arguments[size - 1].helpText(Symbol::HP_Usage, displayOptions, nullptr);
|
||||
ss += "]";
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
|
||||
void ArgumentHolder::addArguments(const std::vector<Argument> &arguments) {
|
||||
Q_D(ArgumentHolder);
|
||||
|
||||
#ifdef SYSCMDLINE_ENABLE_VALIDITY_CHECK
|
||||
for (const auto &arg : arguments) {
|
||||
d->checkAddedArgument(arg);
|
||||
d->arguments.push_back(arg);
|
||||
}
|
||||
#else
|
||||
// d->arguments.insert(d->arguments.end(), arguments.begin(), arguments.end());
|
||||
d->arguments = Utils::concatVector(d->arguments, arguments);
|
||||
#endif
|
||||
}
|
||||
|
||||
ArgumentHolder::ArgumentHolder(ArgumentHolderPrivate *d) : Symbol(d) {
|
||||
}
|
||||
}
|
||||
40
libs/qwindowkit/qmsetup/src/syscmdline/src/argument_p.h
Normal file
40
libs/qwindowkit/qmsetup/src/syscmdline/src/argument_p.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef ARGUMENT_P_H
|
||||
#define ARGUMENT_P_H
|
||||
|
||||
#include "argument.h"
|
||||
#include "symbol_p.h"
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class ArgumentPrivate : public SymbolPrivate {
|
||||
public:
|
||||
ArgumentPrivate(std::string name, const std::string &desc, bool required,
|
||||
Value defaultValue);
|
||||
|
||||
SymbolPrivate *clone() const override;
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
bool required;
|
||||
Value defaultValue;
|
||||
std::vector<Value> expectedValues;
|
||||
std::string displayName;
|
||||
Argument::Number number;
|
||||
Argument::Validator validator;
|
||||
};
|
||||
|
||||
class ArgumentHolderPrivate : public SymbolPrivate {
|
||||
public:
|
||||
ArgumentHolderPrivate(Symbol::SymbolType type, const std::string &desc);
|
||||
|
||||
public:
|
||||
std::vector<Argument> arguments;
|
||||
|
||||
#ifdef SYSCMDLINE_ENABLE_VALIDITY_CHECK
|
||||
void checkAddedArgument(const Argument &arg) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ARGUMENT_P_H
|
||||
480
libs/qwindowkit/qmsetup/src/syscmdline/src/command.cpp
Normal file
480
libs/qwindowkit/qmsetup/src/syscmdline/src/command.cpp
Normal file
@@ -0,0 +1,480 @@
|
||||
#include "command.h"
|
||||
#include "command_p.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#ifdef SYSCMDLINE_ENABLE_VALIDITY_CHECK
|
||||
# include <stdexcept>
|
||||
|
||||
# include "option_p.h"
|
||||
#endif
|
||||
|
||||
#include "parser.h"
|
||||
#include "utils_p.h"
|
||||
#include "option_p.h"
|
||||
#include "strings.h"
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
SharedBasePrivate *CommandCataloguePrivate::clone() const {
|
||||
return new CommandCataloguePrivate(*this);
|
||||
}
|
||||
|
||||
static void addIndexes(GenericMap &indexes, StringList &keys, const std::string &key,
|
||||
const StringList &val) {
|
||||
auto it = indexes.find(key);
|
||||
if (it == indexes.end()) {
|
||||
indexes[key] = ele(new StringList(val));
|
||||
keys.push_back(key);
|
||||
return;
|
||||
}
|
||||
auto list = *it->second.sl;
|
||||
// list.insert(list.end(), val.begin(), val.end());
|
||||
list = Utils::concatVector(list, val);
|
||||
|
||||
#ifdef SYSCMDLINE_ENABLE_VALIDITY_CHECK
|
||||
for (size_t i = 0; i < list.size(); ++i) {
|
||||
for (size_t j = 0; j < i; ++j) {
|
||||
if (list[i] == list[j]) {
|
||||
throw std::runtime_error(Utils::formatText(
|
||||
R"(duplicated items "%1" and "%2" in catalogue)", {
|
||||
list[i],
|
||||
list[j],
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
CommandCatalogue::CommandCatalogue() : SharedBase(new CommandCataloguePrivate()) {
|
||||
}
|
||||
|
||||
void CommandCatalogue::addArguments(const std::string &name, const StringList &args) {
|
||||
Q_D(CommandCatalogue);
|
||||
addIndexes(d->arg.data, d->arguments, name, args);
|
||||
}
|
||||
|
||||
void CommandCatalogue::addOptions(const std::string &name, const StringList &options) {
|
||||
Q_D(CommandCatalogue);
|
||||
addIndexes(d->opt.data, d->options, name, options);
|
||||
}
|
||||
|
||||
void CommandCatalogue::addCommands(const std::string &name, const StringList &commands) {
|
||||
Q_D(CommandCatalogue);
|
||||
addIndexes(d->cmd.data, d->commands, name, commands);
|
||||
}
|
||||
|
||||
CommandPrivate::CommandPrivate(std::string name, const std::string &desc)
|
||||
: ArgumentHolderPrivate(Symbol::ST_Command, desc), name(std::move(name)) {
|
||||
}
|
||||
|
||||
SharedBasePrivate *CommandPrivate::clone() const {
|
||||
return new CommandPrivate(*this);
|
||||
}
|
||||
|
||||
#ifdef SYSCMDLINE_ENABLE_VALIDITY_CHECK
|
||||
void CommandPrivate::checkAddedCommand(const Command &cmd) const {
|
||||
const auto &name = cmd.name();
|
||||
|
||||
// Empty command name?
|
||||
if (name.empty()) {
|
||||
throw std::runtime_error("command doesn't have a name");
|
||||
}
|
||||
|
||||
// Duplicated command name?
|
||||
if (std::any_of(commands.begin(), commands.end(), [&name](const Command &command) {
|
||||
return command.name() == name; //
|
||||
})) {
|
||||
throw std::runtime_error(Utils::formatText("command name \"%1\" duplicated", {name}));
|
||||
}
|
||||
}
|
||||
|
||||
void CommandPrivate::checkAddedOption(const Option &opt,
|
||||
const std::string &exclusiveGroup) const {
|
||||
const auto &d = opt.d_func();
|
||||
|
||||
// Empty token?
|
||||
if (d->tokens.empty()) {
|
||||
throw std::runtime_error("option doesn't have any token");
|
||||
}
|
||||
|
||||
for (const auto &token : d->tokens) {
|
||||
// empty token?
|
||||
if (token.empty()) {
|
||||
throw std::runtime_error("empty option token");
|
||||
}
|
||||
|
||||
// Invalid token?
|
||||
if (!(token.front() == '-' || token.front() == '/')) {
|
||||
throw std::runtime_error(
|
||||
Utils::formatText("option token \"%1\" is invalid", {token}));
|
||||
}
|
||||
|
||||
// Duplicated token?
|
||||
if (std::any_of(options.begin(), options.end(), [&token](const Option &opt) {
|
||||
for (const auto &item : opt.d_func()->tokens)
|
||||
if (item == token)
|
||||
return true;
|
||||
const auto &tokens = opt.d_func()->tokens;
|
||||
return std::any_of(tokens.begin(), tokens.end(),
|
||||
[&token](const std::string &cur) {
|
||||
return cur == token; //
|
||||
});
|
||||
})) {
|
||||
throw std::runtime_error(
|
||||
Utils::formatText("option token \"%1\" duplicated", {token}));
|
||||
}
|
||||
}
|
||||
|
||||
// Global and exclusive option?
|
||||
if (!exclusiveGroup.empty() && opt.isGlobal()) {
|
||||
throw std::runtime_error(Utils::formatText(
|
||||
"global option \"%s\" cannot be in any exclusive group", {opt.token()}));
|
||||
}
|
||||
|
||||
switch (opt.priorLevel()) {
|
||||
case Option::AutoSetWhenNoSymbols: {
|
||||
// Auto-option but required?
|
||||
if (d->required) {
|
||||
throw std::runtime_error(
|
||||
Utils::formatText("auto-option \"%s\" cannot be required", {opt.token()}));
|
||||
}
|
||||
|
||||
// Auto-option with argument?
|
||||
if (!d->arguments.empty()) {
|
||||
throw std::runtime_error(Utils::formatText(
|
||||
"auto-option \"%s\" cannot have any argument", {opt.token()}));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Option::ExclusiveToOptions:
|
||||
case Option::ExclusiveToAll: {
|
||||
// Multiple exclusively prior option?
|
||||
if (opt.isRequired() &&
|
||||
std::any_of(options.begin(), options.end(), [](const Option &opt) {
|
||||
return opt.priorLevel() >= Option::ExclusiveToOptions && opt.isRequired();
|
||||
})) {
|
||||
throw std::runtime_error("at most one exclusively prior and required option");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Inconsistent exclusive group?
|
||||
if (!exclusiveGroup.empty()) {
|
||||
for (size_t i = 0; i < options.size(); ++i) {
|
||||
if (optionGroupNames[i] == exclusiveGroup &&
|
||||
options[i].isRequired() != opt.isRequired()) {
|
||||
throw std::runtime_error(
|
||||
Utils::formatText(R"(option "%1" is %2, but exclusive group "3" isn't)",
|
||||
{
|
||||
opt.token(),
|
||||
opt.isRequired() ? "required" : "required",
|
||||
exclusiveGroup,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Command::Command() : Command({}, {}) {
|
||||
}
|
||||
|
||||
Command::Command(const std::string &name, const std::string &desc,
|
||||
const std::vector<Argument> &args)
|
||||
: ArgumentHolder(new CommandPrivate(name, desc)) {
|
||||
if (!args.empty())
|
||||
addArguments(args);
|
||||
}
|
||||
|
||||
std::string Command::helpText(Symbol::HelpPosition pos, int displayOptions, void *extra) const {
|
||||
Q_D2(Command);
|
||||
if (auto ss = ArgumentHolder::helpText(pos, displayOptions, extra); !ss.empty()) {
|
||||
return ss;
|
||||
}
|
||||
|
||||
switch (pos) {
|
||||
case HP_Usage: {
|
||||
// `extra` should be null or a 3 pointer array
|
||||
auto a = reinterpret_cast<void **>(extra);
|
||||
|
||||
// all options
|
||||
const auto *globalOptions =
|
||||
a ? reinterpret_cast<std::vector<Option> *>(a[0]) : nullptr;
|
||||
auto options = d->options;
|
||||
if (globalOptions) {
|
||||
// options.insert(options.begin(), globalOptions->begin(),
|
||||
// globalOptions->end());
|
||||
options = OptionPrivate::reorderOptions(options, *globalOptions);
|
||||
}
|
||||
StringList groupNames(globalOptions ? globalOptions->size() : 0);
|
||||
// groupNames.insert(groupNames.end(), d->optionGroupNames.begin(),
|
||||
// d->optionGroupNames.end());
|
||||
groupNames = Utils::concatVector(groupNames, d->optionGroupNames);
|
||||
|
||||
auto textProvider = a ? reinterpret_cast<Parser::TextProvider>(a[1]) : nullptr;
|
||||
if (!textProvider) {
|
||||
textProvider = Parser::defaultTextProvider();
|
||||
}
|
||||
|
||||
std::string ss;
|
||||
|
||||
// write command name
|
||||
ss += d->name;
|
||||
|
||||
// Build exclusive option group indexes
|
||||
// group name -> option subscripts (vector<int> *)
|
||||
GenericMap exclusiveGroupIndexes = [](const CommandPrivate *d) {
|
||||
GenericMap res;
|
||||
for (int i = 0; i < d->optionGroupNames.size(); ++i) {
|
||||
const auto &group = d->optionGroupNames[i];
|
||||
if (group.empty())
|
||||
continue;
|
||||
|
||||
auto it = res.find(group);
|
||||
if (it != res.end()) {
|
||||
it->second.il->push_back(i);
|
||||
continue;
|
||||
}
|
||||
res[group] = ele(new IntList({i}));
|
||||
}
|
||||
return res;
|
||||
}(d);
|
||||
|
||||
auto addArgumentsHelp = [&](bool front) {
|
||||
if (bool((displayOptions & Parser::ShowOptionsBehindArguments)) != front &&
|
||||
!d->arguments.empty()) {
|
||||
ss += " " + displayedArguments(displayOptions);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<int> visitedOptions(options.size()); // pointer -> none
|
||||
int visitedCount = 0;
|
||||
auto addVisited = [&visitedOptions, &visitedCount](int idx) {
|
||||
if (visitedOptions[idx])
|
||||
return;
|
||||
visitedOptions[idx] = 1;
|
||||
visitedCount++;
|
||||
};
|
||||
|
||||
auto addExclusiveOptions = [&](int optIdx, bool required) {
|
||||
const auto &opt = options[optIdx];
|
||||
const auto &groupName = groupNames[optIdx];
|
||||
|
||||
do {
|
||||
if (groupName.empty())
|
||||
break;
|
||||
|
||||
const auto &optionIndexes =
|
||||
*exclusiveGroupIndexes.find(groupName)->second.il;
|
||||
if (optionIndexes.size() <= 1)
|
||||
break;
|
||||
|
||||
// Add exclusive groups
|
||||
if (required)
|
||||
ss += "(";
|
||||
|
||||
StringList exclusiveOptions;
|
||||
exclusiveOptions.reserve(optionIndexes.size());
|
||||
for (const auto &idx : optionIndexes) {
|
||||
const auto &curOpt = options[idx];
|
||||
exclusiveOptions.push_back(
|
||||
curOpt.helpText(Symbol::HP_Usage, displayOptions));
|
||||
addVisited(idx);
|
||||
}
|
||||
|
||||
ss += Utils::join(exclusiveOptions, " | ");
|
||||
if (required)
|
||||
ss += ")";
|
||||
return;
|
||||
} while (false);
|
||||
|
||||
// Add single
|
||||
ss += opt.helpText(Symbol::HP_Usage, displayOptions);
|
||||
addVisited(optIdx);
|
||||
};
|
||||
|
||||
auto addOptionsHelp = [&](bool required) {
|
||||
for (int i = 0; i < options.size(); ++i) {
|
||||
const auto &opt = options[i];
|
||||
if (opt.isRequired() != required || visitedOptions[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ss += " ";
|
||||
if (!required)
|
||||
ss += "[";
|
||||
addExclusiveOptions(i, required);
|
||||
if (!required)
|
||||
ss += "]";
|
||||
}
|
||||
};
|
||||
|
||||
int frontOptionInsertionPos = ss.size();
|
||||
|
||||
// write forward arguments
|
||||
addArgumentsHelp(true);
|
||||
|
||||
// write required options
|
||||
if (!(displayOptions & Parser::DontShowRequiredOptionsOnUsage))
|
||||
addOptionsHelp(true);
|
||||
|
||||
// write optional options
|
||||
if ((displayOptions & Parser::ShowOptionalOptionsOnUsage) &&
|
||||
visitedCount < options.size())
|
||||
addOptionsHelp(false);
|
||||
|
||||
// write backward arguments
|
||||
addArgumentsHelp(false);
|
||||
|
||||
if (!d->commands.empty()) {
|
||||
ss += " [" + textProvider(Strings::Token, Strings::OptionalCommands) + "]";
|
||||
}
|
||||
|
||||
if (visitedCount < options.size() || !d->commands.empty()) {
|
||||
std::string optionHint =
|
||||
" [" + textProvider(Strings::Token, Strings::OptionalOptions) + "]";
|
||||
if ((displayOptions & Parser::ShowOptionsHintFront)) {
|
||||
ss = ss.substr(0, frontOptionInsertionPos) + optionHint +
|
||||
ss.substr(frontOptionInsertionPos);
|
||||
} else {
|
||||
ss += optionHint;
|
||||
}
|
||||
}
|
||||
|
||||
// release indexes
|
||||
for (const auto &pair : std::as_const(exclusiveGroupIndexes)) {
|
||||
delete pair.second.il;
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
case HP_ErrorText:
|
||||
case HP_FirstColumn: {
|
||||
return d->name;
|
||||
}
|
||||
case HP_SecondColumn: {
|
||||
return d->desc;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string Command::name() const {
|
||||
Q_D2(Command);
|
||||
return d->name;
|
||||
}
|
||||
|
||||
void Command::setName(const std::string &name) {
|
||||
Q_D(Command);
|
||||
d->name = name;
|
||||
}
|
||||
|
||||
void Command::addOptions(const std::vector<Option> &options, const std::string &group) {
|
||||
Q_D(Command);
|
||||
#ifdef SYSCMDLINE_ENABLE_VALIDITY_CHECK
|
||||
for (const auto &opt : options) {
|
||||
d->checkAddedOption(opt, group);
|
||||
d->options.push_back(opt);
|
||||
d->optionGroupNames.push_back(group);
|
||||
}
|
||||
#else
|
||||
// d->options.insert(d->options.end(), options.begin(), options.end());
|
||||
// d->optionGroupNames.insert(d->optionGroupNames.end(), options.size(), group);
|
||||
d->options = Utils::concatVector(d->options, options);
|
||||
d->optionGroupNames = Utils::concatVector(d->optionGroupNames, {options.size(), group});
|
||||
#endif
|
||||
}
|
||||
|
||||
int Command::commandCount() const {
|
||||
Q_D2(Command);
|
||||
return int(d->commands.size());
|
||||
}
|
||||
|
||||
Command Command::command(int index) const {
|
||||
Q_D2(Command);
|
||||
return d->commands[index];
|
||||
}
|
||||
|
||||
void Command::addCommands(const std::vector<Command> &commands) {
|
||||
Q_D(Command);
|
||||
#ifdef SYSCMDLINE_ENABLE_VALIDITY_CHECK
|
||||
for (const auto &cmd : commands) {
|
||||
d->checkAddedCommand(cmd);
|
||||
d->commands.push_back(cmd);
|
||||
}
|
||||
#else
|
||||
// d->commands.insert(d->commands.end(), commands.begin(), commands.end());
|
||||
d->commands = Utils::concatVector(d->commands, commands);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string Command::detailedDescription() const {
|
||||
Q_D2(Command);
|
||||
return d->detailedDescription;
|
||||
}
|
||||
|
||||
void Command::setDetailedDescription(const std::string &detailedDescription) {
|
||||
Q_D(Command);
|
||||
d->detailedDescription = detailedDescription;
|
||||
}
|
||||
|
||||
Command::Handler Command::handler() const {
|
||||
Q_D2(Command);
|
||||
return d->handler;
|
||||
}
|
||||
|
||||
void Command::setHandler(const Handler &handler) {
|
||||
Q_D(Command);
|
||||
d->handler = handler;
|
||||
}
|
||||
|
||||
CommandCatalogue Command::catalogue() const {
|
||||
Q_D2(Command);
|
||||
return d->catalogue;
|
||||
}
|
||||
|
||||
void Command::setCatalogue(const CommandCatalogue &catalogue) {
|
||||
Q_D(Command);
|
||||
d->catalogue = catalogue;
|
||||
}
|
||||
|
||||
std::string Command::versionString() const {
|
||||
Q_D2(Command);
|
||||
return d->version;
|
||||
}
|
||||
|
||||
void Command::addVersionOption(const std::string &version, const StringList &tokens,
|
||||
const std::string &desc) {
|
||||
Q_D(Command);
|
||||
d->version = version;
|
||||
|
||||
Option versionOption(Option::Version, tokens, desc);
|
||||
versionOption.setPriorLevel(Option::IgnoreMissingSymbols);
|
||||
addOption(versionOption);
|
||||
}
|
||||
|
||||
void Command::addHelpOption(bool showHelpIfNoArg, bool global, const StringList &tokens,
|
||||
const std::string &desc) {
|
||||
Q_D(Command);
|
||||
Option helpOption(Option::Help, tokens, desc);
|
||||
helpOption.setPriorLevel(showHelpIfNoArg ? Option::AutoSetWhenNoSymbols
|
||||
: Option::IgnoreMissingSymbols);
|
||||
helpOption.setGlobal(global);
|
||||
addOption(helpOption);
|
||||
}
|
||||
|
||||
HelpLayout Command::helpLayout() const {
|
||||
Q_D2(Command);
|
||||
return d->helpLayout;
|
||||
}
|
||||
|
||||
void Command::setHelpLayout(const HelpLayout &helpLayout) {
|
||||
Q_D(Command);
|
||||
d->helpLayout = helpLayout;
|
||||
}
|
||||
|
||||
}
|
||||
53
libs/qwindowkit/qmsetup/src/syscmdline/src/command_p.h
Normal file
53
libs/qwindowkit/qmsetup/src/syscmdline/src/command_p.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef COMMAND_P_H
|
||||
#define COMMAND_P_H
|
||||
|
||||
#include "argument_p.h"
|
||||
#include "command.h"
|
||||
|
||||
#include "map_p.h"
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class CommandCataloguePrivate : public SharedBasePrivate {
|
||||
public:
|
||||
SharedBasePrivate *clone() const;
|
||||
|
||||
StringListMapWrapper arg;
|
||||
StringListMapWrapper opt;
|
||||
StringListMapWrapper cmd;
|
||||
|
||||
StringList arguments;
|
||||
StringList options;
|
||||
StringList commands;
|
||||
};
|
||||
|
||||
class CommandPrivate : public ArgumentHolderPrivate {
|
||||
public:
|
||||
CommandPrivate(std::string name, const std::string &desc);
|
||||
|
||||
SharedBasePrivate *clone() const override;
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
|
||||
std::vector<Option> options;
|
||||
std::vector<std::string> optionGroupNames;
|
||||
std::vector<Command> commands;
|
||||
|
||||
std::string version;
|
||||
std::string detailedDescription;
|
||||
CommandCatalogue catalogue;
|
||||
|
||||
Command::Handler handler;
|
||||
|
||||
HelpLayout helpLayout;
|
||||
|
||||
#ifdef SYSCMDLINE_ENABLE_VALIDITY_CHECK
|
||||
void checkAddedCommand(const Command &cmd) const;
|
||||
void checkAddedOption(const Option &opt, const std::string &exclusiveGroup) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // COMMAND_P_H
|
||||
161
libs/qwindowkit/qmsetup/src/syscmdline/src/helplayout.cpp
Normal file
161
libs/qwindowkit/qmsetup/src/syscmdline/src/helplayout.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#include "helplayout.h"
|
||||
#include "helplayout_p.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "system.h"
|
||||
#include "parser_p.h"
|
||||
#include "utils_p.h"
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
static inline void printLast(bool hasNext) {
|
||||
if (hasNext)
|
||||
u8info("\n");
|
||||
}
|
||||
|
||||
static void defaultTextsPrinter(MessageType messageType, bool highlight,
|
||||
const HelpLayout::Context &ctx) {
|
||||
if (ctx.text->lines.empty())
|
||||
return;
|
||||
if (ctx.text->title.empty()) {
|
||||
// Content
|
||||
u8debug(messageType, highlight, "%s\n", ctx.text->lines.data());
|
||||
} else {
|
||||
// Title
|
||||
u8debug(messageType, highlight, "%s:\n", ctx.text->title.data());
|
||||
|
||||
// Content
|
||||
auto lines = Utils::split(ctx.text->lines, "\n");
|
||||
for (const auto &line : std::as_const(lines))
|
||||
u8debug(messageType, highlight, "%s%s\n", ctx.parser->d_func()->indent().data(),
|
||||
line.data());
|
||||
}
|
||||
printLast(ctx.hasNext);
|
||||
}
|
||||
|
||||
static void defaultInfoPrinter(const HelpLayout::Context &ctx) {
|
||||
defaultTextsPrinter(MT_Debug, false, ctx);
|
||||
}
|
||||
|
||||
static void defaultWarnPrinter(const HelpLayout::Context &ctx) {
|
||||
defaultTextsPrinter(MT_Warning, false, ctx);
|
||||
}
|
||||
|
||||
static void defaultErrorPrinter(const HelpLayout::Context &ctx) {
|
||||
defaultTextsPrinter(MT_Critical, true, ctx);
|
||||
}
|
||||
|
||||
static void defaultListPrinter(const HelpLayout::Context &ctx) {
|
||||
if (ctx.list->firstColumn.empty())
|
||||
return;
|
||||
|
||||
// Title
|
||||
u8info("%s:\n", ctx.text->title.data());
|
||||
|
||||
const auto &list = ctx.list;
|
||||
const auto &parserData = ctx.parser->d_func();
|
||||
|
||||
int widest = ctx.firstColumnLength;
|
||||
if (widest == 0) {
|
||||
for (const auto &item : list->firstColumn)
|
||||
widest = std::max(widest, int(item.size()));
|
||||
}
|
||||
|
||||
std::vector<std::string> res;
|
||||
for (size_t i = 0; i < list->firstColumn.size(); ++i) {
|
||||
const auto &first = list->firstColumn[i];
|
||||
const auto &second = list->secondColumn[i];
|
||||
|
||||
auto lines = Utils::split(second, "\n");
|
||||
if (lines.empty())
|
||||
lines.emplace_back();
|
||||
|
||||
{
|
||||
std::string ss;
|
||||
ss += parserData->indent();
|
||||
ss += first;
|
||||
ss += std::string(widest - first.size(), ' ');
|
||||
ss += parserData->spacing();
|
||||
ss += lines.front();
|
||||
res.push_back(ss);
|
||||
}
|
||||
|
||||
for (size_t j = 1; j < lines.size(); ++j) {
|
||||
std::string ss;
|
||||
ss += parserData->indent();
|
||||
ss += std::string(widest, ' ');
|
||||
ss += parserData->spacing();
|
||||
ss += std::as_const(lines)[j];
|
||||
res.push_back(ss);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &line : std::as_const(res)) {
|
||||
u8info("%s\n", line.data());
|
||||
}
|
||||
|
||||
printLast(ctx.hasNext);
|
||||
}
|
||||
|
||||
HelpLayout::HelpLayout() : SharedBase(new HelpLayoutPrivate()) {
|
||||
}
|
||||
|
||||
void HelpLayout::addHelpTextItem(HelpTextItem type, const Output &out) {
|
||||
Q_D(HelpLayout);
|
||||
d->itemDataList.push_back(
|
||||
{HelpLayoutPrivate::HelpText, type, out ? out : defaultInfoPrinter, {}, {}});
|
||||
}
|
||||
void HelpLayout::addHelpListItem(HelpListItem type, const Output &out) {
|
||||
Q_D(HelpLayout);
|
||||
d->itemDataList.push_back(
|
||||
{HelpLayoutPrivate::HelpList, type, out ? out : defaultListPrinter, {}, {}});
|
||||
}
|
||||
void HelpLayout::addMessageItem(MessageItem type, const Output &out) {
|
||||
Q_D(HelpLayout);
|
||||
d->itemDataList.push_back({HelpLayoutPrivate::Message, type, out ? out : [](MessageItem item) {
|
||||
switch (item) {
|
||||
case MI_Warning:
|
||||
return defaultWarnPrinter;
|
||||
case MI_Critical:
|
||||
return defaultErrorPrinter;
|
||||
default:
|
||||
break ;
|
||||
}
|
||||
return defaultInfoPrinter;
|
||||
}(type), {}, {}});
|
||||
}
|
||||
void HelpLayout::addUserHelpTextItem(const Text &text, const Output &out) {
|
||||
Q_D(HelpLayout);
|
||||
d->itemDataList.push_back(
|
||||
{HelpLayoutPrivate::UserHelpText, 0, out ? out : defaultInfoPrinter, text, {}});
|
||||
}
|
||||
void HelpLayout::addUserHelpListItem(const List &list, const Output &out) {
|
||||
Q_D(HelpLayout);
|
||||
d->itemDataList.push_back(
|
||||
{HelpLayoutPrivate::UserHelpList, 0, out ? out : defaultListPrinter, {}, list});
|
||||
}
|
||||
void HelpLayout::addUserIntroItem(const Output &out) {
|
||||
Q_D(HelpLayout);
|
||||
d->itemDataList.push_back({HelpLayoutPrivate::UserIntro, 0, out, {}, {}});
|
||||
}
|
||||
|
||||
HelpLayout HelpLayout::defaultHelpLayout() {
|
||||
static HelpLayout instance = []() {
|
||||
HelpLayout res;
|
||||
res.addHelpTextItem(HT_Prologue);
|
||||
res.addMessageItem(MI_Information);
|
||||
res.addMessageItem(MI_Warning);
|
||||
res.addMessageItem(MI_Critical);
|
||||
res.addHelpTextItem(HT_Description);
|
||||
res.addHelpTextItem(HT_Usage);
|
||||
res.addHelpListItem(HL_Arguments);
|
||||
res.addHelpListItem(HL_Options);
|
||||
res.addHelpListItem(HL_Commands);
|
||||
res.addHelpTextItem(HT_Epilogue);
|
||||
return res;
|
||||
}();
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
41
libs/qwindowkit/qmsetup/src/syscmdline/src/helplayout_p.h
Normal file
41
libs/qwindowkit/qmsetup/src/syscmdline/src/helplayout_p.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef HELPLAYOUT_P_H
|
||||
#define HELPLAYOUT_P_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "sharedbase_p.h"
|
||||
#include "helplayout.h"
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class HelpLayoutPrivate : public SharedBasePrivate {
|
||||
public:
|
||||
HelpLayoutPrivate *clone() const {
|
||||
return new HelpLayoutPrivate(*this);
|
||||
}
|
||||
|
||||
enum ItemType {
|
||||
HelpText,
|
||||
HelpList,
|
||||
Message,
|
||||
UserHelpText,
|
||||
UserHelpList,
|
||||
UserIntro,
|
||||
};
|
||||
|
||||
struct ItemData {
|
||||
ItemType itemType;
|
||||
int index;
|
||||
HelpLayout::Output out;
|
||||
|
||||
// Maybe
|
||||
HelpLayout::Text text;
|
||||
HelpLayout::List list;
|
||||
};
|
||||
|
||||
std::vector<ItemData> itemDataList;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HELPLAYOUT_P_H
|
||||
114
libs/qwindowkit/qmsetup/src/syscmdline/src/map_p.h
Normal file
114
libs/qwindowkit/qmsetup/src/syscmdline/src/map_p.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#ifndef MAP_P_H
|
||||
#define MAP_P_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
using StringList = std::vector<std::string>;
|
||||
|
||||
using IntList = std::vector<int>;
|
||||
|
||||
union Ele {
|
||||
int i;
|
||||
size_t s;
|
||||
IntList *il;
|
||||
StringList *sl;
|
||||
void *p;
|
||||
};
|
||||
|
||||
inline Ele ele(int i) noexcept {
|
||||
#if __cplusplus >= 202002L
|
||||
return Ele{.i = i};
|
||||
#else
|
||||
Ele res;
|
||||
res.i = i;
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline Ele ele(size_t s) noexcept {
|
||||
#if __cplusplus >= 202002L
|
||||
return Ele{.s = s};
|
||||
#else
|
||||
Ele res;
|
||||
res.s = s;
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline Ele ele(IntList *il) noexcept {
|
||||
#if __cplusplus >= 202002L
|
||||
return Ele{.il = il};
|
||||
#else
|
||||
Ele res;
|
||||
res.il = il;
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline Ele ele(StringList *sl) noexcept {
|
||||
#if __cplusplus >= 202002L
|
||||
return Ele{.sl = sl};
|
||||
#else
|
||||
Ele res;
|
||||
res.sl = sl;
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline Ele ele(void *p) noexcept {
|
||||
#if __cplusplus >= 202002L
|
||||
return Ele{.p = p};
|
||||
#else
|
||||
Ele res;
|
||||
res.p = p;
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Avoiding template specialization greatly helps to reduce the binary size,
|
||||
// so we only use this map in the library implementation.
|
||||
using GenericMap = std::map<std::string, Ele>;
|
||||
|
||||
struct StringListMapWrapper {
|
||||
StringListMapWrapper() = default;
|
||||
|
||||
StringListMapWrapper(const StringListMapWrapper &other) {
|
||||
for (const auto &pair : other.data) {
|
||||
data[pair.first] = ele(new StringList(*pair.second.sl));
|
||||
}
|
||||
}
|
||||
|
||||
~StringListMapWrapper() {
|
||||
for (const auto &pair : std::as_const(data)) {
|
||||
delete pair.second.sl;
|
||||
}
|
||||
}
|
||||
|
||||
GenericMap data;
|
||||
};
|
||||
|
||||
struct IntListMapWrapper {
|
||||
IntListMapWrapper() = default;
|
||||
|
||||
IntListMapWrapper(const IntListMapWrapper &other) {
|
||||
for (const auto &pair : other.data) {
|
||||
data[pair.first] = ele(new IntList(*pair.second.il));
|
||||
}
|
||||
}
|
||||
|
||||
~IntListMapWrapper() {
|
||||
for (const auto &pair : std::as_const(data)) {
|
||||
delete pair.second.il;
|
||||
}
|
||||
}
|
||||
|
||||
GenericMap data;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MAP_P_H
|
||||
214
libs/qwindowkit/qmsetup/src/syscmdline/src/option.cpp
Normal file
214
libs/qwindowkit/qmsetup/src/syscmdline/src/option.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
#include "option.h"
|
||||
#include "option_p.h"
|
||||
|
||||
#include "utils_p.h"
|
||||
#include "strings.h"
|
||||
#include "parser.h"
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
static std::vector<std::string> defaultTokensForRole(Option::Role role) {
|
||||
std::vector<std::string> res;
|
||||
switch (role) {
|
||||
case Option::Help:
|
||||
res = {"-h", "--help"};
|
||||
break;
|
||||
case Option::Version:
|
||||
res = {"-v", "--version"};
|
||||
break;
|
||||
case Option::Verbose:
|
||||
res = {"-V", "--verbose"};
|
||||
break;
|
||||
case Option::Debug:
|
||||
res = {"-d", "--debug"};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
OptionPrivate::OptionPrivate(Option::Role role, const std::vector<std::string> &tokens,
|
||||
const std::string &desc, bool required)
|
||||
: ArgumentHolderPrivate(Symbol::ST_Option, desc), role(role), tokens(tokens),
|
||||
required(required), shortMatchRule(Option::NoShortMatch), priorLevel(Option::NoPrior),
|
||||
global(false), maxOccurrence(1) {
|
||||
}
|
||||
|
||||
SymbolPrivate *OptionPrivate::clone() const {
|
||||
return new OptionPrivate(*this);
|
||||
}
|
||||
|
||||
std::vector<Option> OptionPrivate::reorderOptions(const std::vector<Option> &options,
|
||||
const std::vector<Option> &globalOptions) {
|
||||
std::vector<Option> res;
|
||||
std::vector<Option> roles;
|
||||
for (const auto &opt : options) {
|
||||
if (opt.role() == Option::NoRole) {
|
||||
res.push_back(opt);
|
||||
continue;
|
||||
}
|
||||
roles.push_back(opt);
|
||||
}
|
||||
|
||||
for (const auto &opt : globalOptions) {
|
||||
if (opt.role() == Option::NoRole) {
|
||||
res.push_back(opt);
|
||||
continue;
|
||||
}
|
||||
roles.push_back(opt);
|
||||
}
|
||||
|
||||
for (const auto &opt : std::as_const(roles)) {
|
||||
res.push_back(opt);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// template <typename... Args>
|
||||
// Option(const char *firstToken, Args &&...args, const std::string &desc)
|
||||
// : Option(std::vector<std::string>(), desc) {
|
||||
// std::vector<std::string> tokens;
|
||||
// tokens.emplace_back(firstToken);
|
||||
// (tokens.emplace_back(std::forward<Args>(args)), ...);
|
||||
// setTokens(tokens);
|
||||
// }
|
||||
|
||||
Option::Option() : Option(NoRole, {}) {
|
||||
}
|
||||
|
||||
Option::Option(Role role, const std::vector<std::string> &tokens, const std::string &desc)
|
||||
: ArgumentHolder(new OptionPrivate(
|
||||
role, tokens.empty() ? defaultTokensForRole(role) : tokens, desc, false)) {
|
||||
}
|
||||
|
||||
Option::Option(const std::string &token, const std::string &desc, const Argument &arg,
|
||||
bool required)
|
||||
: Option(std::vector<std::string>{token}, desc, arg, required) {
|
||||
}
|
||||
|
||||
Option::Option(const std::vector<std::string> &tokens, const std::string &desc,
|
||||
const Argument &arg, bool required)
|
||||
: ArgumentHolder(new OptionPrivate(NoRole, tokens, desc, required)) {
|
||||
if (!arg.d_func()->name.empty())
|
||||
addArgument(arg);
|
||||
}
|
||||
|
||||
std::string Option::helpText(Symbol::HelpPosition pos, int displayOptions, void *extra) const {
|
||||
Q_D2(Option);
|
||||
if (auto ss = ArgumentHolder::helpText(pos, displayOptions, extra); !ss.empty()) {
|
||||
return ss;
|
||||
}
|
||||
|
||||
switch (pos) {
|
||||
case Symbol::HP_Usage: {
|
||||
std::string appendix;
|
||||
if (!d->arguments.empty()) {
|
||||
appendix = " " + displayedArguments(displayOptions);
|
||||
}
|
||||
return d->tokens.front() + appendix;
|
||||
}
|
||||
case Symbol::HP_ErrorText: {
|
||||
return d->tokens.front();
|
||||
}
|
||||
case Symbol::HP_FirstColumn: {
|
||||
std::string appendix;
|
||||
if (!d->arguments.empty()) {
|
||||
appendix = " " + displayedArguments(displayOptions);
|
||||
}
|
||||
return Utils::join(d->tokens, ", ") + appendix;
|
||||
}
|
||||
case Symbol::HP_SecondColumn: {
|
||||
auto textProvider = reinterpret_cast<Parser::TextProvider>(extra);
|
||||
if (!textProvider) {
|
||||
textProvider = Parser::defaultTextProvider();
|
||||
}
|
||||
|
||||
std::string appendix;
|
||||
std::string desc = d->desc;
|
||||
if (desc.empty() && d->role != NoRole) {
|
||||
desc = textProvider(Strings::OptionRole,
|
||||
static_cast<Strings::OptionRoleText>(d->role));
|
||||
}
|
||||
|
||||
// Required
|
||||
if (d->required && (displayOptions & Parser::ShowOptionIsRequired)) {
|
||||
appendix += " [" + textProvider(Strings::Title, Strings::Required) + "]";
|
||||
}
|
||||
return desc + appendix;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Option::Role Option::role() const {
|
||||
Q_D2(Option);
|
||||
return d->role;
|
||||
}
|
||||
|
||||
void Option::setRole(Role role) {
|
||||
Q_D(Option);
|
||||
d->role = role;
|
||||
}
|
||||
|
||||
const std::vector<std::string> &Option::tokens() const {
|
||||
Q_D2(Option);
|
||||
return d->tokens;
|
||||
}
|
||||
|
||||
void Option::setTokens(const std::vector<std::string> &tokens) {
|
||||
Q_D(Option);
|
||||
d->tokens = tokens;
|
||||
}
|
||||
|
||||
bool Option::isRequired() const {
|
||||
Q_D2(Option);
|
||||
return d->required;
|
||||
}
|
||||
|
||||
void Option::setRequired(bool required) {
|
||||
Q_D(Option);
|
||||
d->required = required;
|
||||
}
|
||||
|
||||
Option::ShortMatchRule Option::shortMatchRule() const {
|
||||
Q_D2(Option);
|
||||
return d->shortMatchRule;
|
||||
}
|
||||
|
||||
void Option::setShortMatchRule(ShortMatchRule shortMatchRule) {
|
||||
Q_D(Option);
|
||||
d->shortMatchRule = shortMatchRule;
|
||||
}
|
||||
|
||||
Option::PriorLevel Option::priorLevel() const {
|
||||
Q_D2(Option);
|
||||
return d->priorLevel;
|
||||
}
|
||||
|
||||
void Option::setPriorLevel(PriorLevel priorLevel) {
|
||||
Q_D(Option);
|
||||
d->priorLevel = priorLevel;
|
||||
}
|
||||
|
||||
bool Option::isGlobal() const {
|
||||
Q_D2(Option);
|
||||
return d->global;
|
||||
}
|
||||
|
||||
void Option::setGlobal(bool on) {
|
||||
Q_D(Option);
|
||||
d->global = on;
|
||||
}
|
||||
|
||||
int Option::maxOccurrence() const {
|
||||
Q_D2(Option);
|
||||
return d->maxOccurrence;
|
||||
}
|
||||
|
||||
void Option::setMaxOccurrence(int max) {
|
||||
Q_D(Option);
|
||||
d->maxOccurrence = max;
|
||||
}
|
||||
|
||||
}
|
||||
31
libs/qwindowkit/qmsetup/src/syscmdline/src/option_p.h
Normal file
31
libs/qwindowkit/qmsetup/src/syscmdline/src/option_p.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef OPTION_P_H
|
||||
#define OPTION_P_H
|
||||
|
||||
#include "argument_p.h"
|
||||
#include "option.h"
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class OptionPrivate : public ArgumentHolderPrivate {
|
||||
public:
|
||||
OptionPrivate(Option::Role role, const std::vector<std::string> &tokens,
|
||||
const std::string &desc, bool required);
|
||||
|
||||
SymbolPrivate *clone() const override;
|
||||
|
||||
public:
|
||||
Option::Role role;
|
||||
std::vector<std::string> tokens;
|
||||
bool required;
|
||||
Option::ShortMatchRule shortMatchRule;
|
||||
Option::PriorLevel priorLevel;
|
||||
bool global;
|
||||
int maxOccurrence;
|
||||
|
||||
static std::vector<Option> reorderOptions(const std::vector<Option> &options,
|
||||
const std::vector<Option> &globalOptions);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OPTION_P_H
|
||||
1152
libs/qwindowkit/qmsetup/src/syscmdline/src/parser.cpp
Normal file
1152
libs/qwindowkit/qmsetup/src/syscmdline/src/parser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
37
libs/qwindowkit/qmsetup/src/syscmdline/src/parser_p.h
Normal file
37
libs/qwindowkit/qmsetup/src/syscmdline/src/parser_p.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef PARSER_P_H
|
||||
#define PARSER_P_H
|
||||
|
||||
#include "sharedbase_p.h"
|
||||
#include "parser.h"
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class ParserPrivate : public SharedBasePrivate {
|
||||
public:
|
||||
ParserPrivate();
|
||||
ParserPrivate *clone() const;
|
||||
|
||||
Command rootCommand;
|
||||
std::string prologue, epilogue;
|
||||
int displayOptions;
|
||||
|
||||
int sizeConfig[3] = {
|
||||
4,
|
||||
4,
|
||||
80,
|
||||
};
|
||||
|
||||
Parser::TextProvider textProvider;
|
||||
|
||||
inline std::string indent() const {
|
||||
return std::string(sizeConfig[Parser::ST_Indent], ' ');
|
||||
}
|
||||
|
||||
inline std::string spacing() const {
|
||||
return std::string(sizeConfig[Parser::ST_Spacing], ' ');
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // PARSER_P_H
|
||||
725
libs/qwindowkit/qmsetup/src/syscmdline/src/parseresult.cpp
Normal file
725
libs/qwindowkit/qmsetup/src/syscmdline/src/parseresult.cpp
Normal file
@@ -0,0 +1,725 @@
|
||||
#include "parseresult.h"
|
||||
#include "parseresult_p.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "utils_p.h"
|
||||
#include "strings.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "parser_p.h"
|
||||
#include "option_p.h"
|
||||
#include "helplayout_p.h"
|
||||
#include "command_p.h"
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
const OptionData &OptionData::sharedNull() {
|
||||
static OptionData _data;
|
||||
return _data;
|
||||
}
|
||||
|
||||
static const std::vector<Value> &sharedEmptyValues() {
|
||||
static std::vector<Value> _data;
|
||||
return _data;
|
||||
}
|
||||
|
||||
OptionResult::OptionResult() : data(&OptionData::sharedNull()) {
|
||||
}
|
||||
|
||||
Option OptionResult::option() const {
|
||||
auto &v = *reinterpret_cast<const OptionData *>(data);
|
||||
return v.option ? *v.option : Option();
|
||||
}
|
||||
|
||||
int OptionResult::indexOf(const std::string &name) const {
|
||||
auto &v = *reinterpret_cast<const OptionData *>(data);
|
||||
auto it = v.argNameIndexes.find(name);
|
||||
if (it == v.argNameIndexes.end())
|
||||
return -1;
|
||||
return it->second.i;
|
||||
}
|
||||
|
||||
int OptionResult::count() const {
|
||||
auto &v = *reinterpret_cast<const OptionData *>(data);
|
||||
return v.count;
|
||||
}
|
||||
|
||||
std::vector<Value> OptionResult::allValues(int index) const {
|
||||
auto &v = *reinterpret_cast<const OptionData *>(data);
|
||||
if (index < 0 || index >= v.argSize)
|
||||
return {};
|
||||
|
||||
std::vector<Value> allValues;
|
||||
for (int i = 0; i < v.count; ++i) {
|
||||
const auto &values = v.argResult[i][index];
|
||||
// allValues.insert(allValues.end(), values.begin(), values.end());
|
||||
allValues = Utils::concatVector(allValues, values);
|
||||
}
|
||||
return allValues;
|
||||
}
|
||||
|
||||
const std::vector<Value> &OptionResult::values(int index, int n) const {
|
||||
auto &v = *reinterpret_cast<const OptionData *>(data);
|
||||
if (index < 0 || index >= v.argSize)
|
||||
return sharedEmptyValues();
|
||||
if (n < 0 || n >= v.count)
|
||||
return sharedEmptyValues();
|
||||
return v.argResult[n][index];
|
||||
}
|
||||
|
||||
Value OptionResult::value(int index, int n) const {
|
||||
auto &v = *reinterpret_cast<const OptionData *>(data);
|
||||
if (index < 0 || index >= v.argSize)
|
||||
return {};
|
||||
if (n < 0 || n >= v.count)
|
||||
return v.option->d_func()->arguments[index].defaultValue();
|
||||
const auto &args = v.argResult[n][index];
|
||||
return args.empty() ? Value() : args.front();
|
||||
}
|
||||
|
||||
std::string ParseResultPrivate::correctionText() const {
|
||||
std::vector<std::string> expectedValues;
|
||||
switch (error) {
|
||||
case ParseResult::UnknownOption: {
|
||||
for (int i = 0; i < core.allOptionsSize; ++i) {
|
||||
const auto &opt = *core.allOptionsResult[i].option;
|
||||
for (const auto &token : opt.d_func()->tokens) {
|
||||
expectedValues.push_back(token);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ParseResult::InvalidArgumentValue: {
|
||||
auto d = errorArgument->d_func();
|
||||
for (const auto &item : d->expectedValues) {
|
||||
expectedValues.push_back(item.toString());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ParseResult::UnknownCommand: {
|
||||
for (const auto &cmd : command->d_func()->commands) {
|
||||
expectedValues.push_back(cmd.name());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
|
||||
auto input = errorPlaceholders[0];
|
||||
auto suggestions = Utils::calcClosestTexts(expectedValues, input, int(input.size()) / 2);
|
||||
if (suggestions.empty())
|
||||
return {};
|
||||
|
||||
std::string ss;
|
||||
const auto &parserData = parser.d_func();
|
||||
ss += Utils::formatText(
|
||||
parserData->textProvider(Strings::Information, Strings::MatchCommand), {input});
|
||||
for (const auto &item : std::as_const(suggestions)) {
|
||||
ss += "\n" + parserData->indent() + item;
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
|
||||
std::vector<Option> ParseResultPrivate::globalOptions() const {
|
||||
std::vector<Option> res;
|
||||
res.reserve(core.globalOptionsSize);
|
||||
for (int i = 0; i < core.globalOptionsSize; ++i) {
|
||||
res.push_back(*core.allOptionsResult[i].option);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void ParseResultPrivate::showMessage(const std::string &info, const std::string &warn,
|
||||
const std::string &err, bool isMsg) const {
|
||||
// Make it as a POD structure
|
||||
struct Lists {
|
||||
HelpLayout::List *data;
|
||||
int size;
|
||||
};
|
||||
|
||||
// Avoid using `std::function` to reduce binary size
|
||||
const auto &getLists = [](int displayOptions, const GenericMap &catalog,
|
||||
const StringList &catalogNames, // catalog
|
||||
|
||||
const GenericMap &symbolIndexes, // name -> index
|
||||
int symbolCount, const Symbol *(*getter)(int, const void *),
|
||||
const void *user, // get symbol from index
|
||||
|
||||
std::string (*getName)(const Symbol *), // get name of symbol
|
||||
int *maxWidth, void *extra,
|
||||
const std::string &defaultTitle) -> Lists {
|
||||
Lists res;
|
||||
int index = 0;
|
||||
res.data = new HelpLayout::List[catalogNames.size() + 1];
|
||||
|
||||
// symbol indexes
|
||||
std::vector<int> visitedIndexes(symbolCount);
|
||||
|
||||
// catalogues
|
||||
for (const auto &catalogName : catalogNames) {
|
||||
auto &list = res.data[index];
|
||||
list.title = catalogName;
|
||||
|
||||
auto &symbolNames = *catalog.find(catalogName)->second.sl;
|
||||
bool empty = true;
|
||||
for (const auto &name : symbolNames) {
|
||||
auto it = symbolIndexes.find(name);
|
||||
if (it == symbolIndexes.end())
|
||||
continue;
|
||||
|
||||
auto idx = it->second.i;
|
||||
const auto &sym = getter(idx, user);
|
||||
auto first = sym->helpText(Symbol::HP_FirstColumn, displayOptions, extra);
|
||||
auto second = sym->helpText(Symbol::HP_SecondColumn, displayOptions, extra);
|
||||
*maxWidth = std::max(int(first.size()), *maxWidth);
|
||||
list.firstColumn.emplace_back(std::move(first));
|
||||
list.secondColumn.emplace_back(std::move(second));
|
||||
visitedIndexes[idx] = 1;
|
||||
empty = false;
|
||||
}
|
||||
|
||||
if (!empty)
|
||||
index++; // index inc
|
||||
}
|
||||
|
||||
// rest
|
||||
{
|
||||
auto &list = res.data[index];
|
||||
list.title = defaultTitle;
|
||||
|
||||
bool empty = true;
|
||||
for (int i = 0; i < symbolCount; ++i) {
|
||||
if (visitedIndexes[i])
|
||||
continue;
|
||||
|
||||
const auto &sym = getter(i, user);
|
||||
auto first = sym->helpText(Symbol::HP_FirstColumn, displayOptions, extra);
|
||||
auto second = sym->helpText(Symbol::HP_SecondColumn, displayOptions, extra);
|
||||
*maxWidth = std::max(int(first.size()), *maxWidth);
|
||||
list.firstColumn.emplace_back(std::move(first));
|
||||
list.secondColumn.emplace_back(std::move(second));
|
||||
empty = false;
|
||||
}
|
||||
|
||||
if (!empty)
|
||||
index++; // index inc
|
||||
}
|
||||
|
||||
res.size = index;
|
||||
return res;
|
||||
};
|
||||
|
||||
const auto &d = command->d_func();
|
||||
const auto &parserData = parser.d_func();
|
||||
const auto &catalogueData = d->catalogue.d_func();
|
||||
const auto displayOptions = parserData->displayOptions;
|
||||
|
||||
bool noHelp = isMsg && (displayOptions & Parser::DontShowHelpOnError);
|
||||
bool noIntro = isMsg && (displayOptions & Parser::DontShowIntroOnError);
|
||||
|
||||
int maxWidth = 0;
|
||||
auto reorderedOptions = OptionPrivate::reorderOptions(d->options, globalOptions());
|
||||
|
||||
// Alloc
|
||||
|
||||
Lists argLists =
|
||||
noHelp ? Lists{nullptr, 0}
|
||||
: getLists(
|
||||
displayOptions, catalogueData->arg.data, catalogueData->arguments,
|
||||
core.argNameIndexes, int(d->arguments.size()),
|
||||
[](int i, const void *user) -> const Symbol * {
|
||||
return &reinterpret_cast<decltype(d)>(user)->arguments[i]; //
|
||||
},
|
||||
d,
|
||||
[](const Symbol *s) {
|
||||
return static_cast<const Argument *>(s)->name(); //
|
||||
},
|
||||
&maxWidth, reinterpret_cast<void *>(parserData->textProvider),
|
||||
parserData->textProvider(Strings::Title, Strings::Arguments));
|
||||
|
||||
Lists optLists =
|
||||
noHelp
|
||||
? Lists{nullptr, 0}
|
||||
: getLists(
|
||||
displayOptions, catalogueData->opt.data, catalogueData->options,
|
||||
core.allOptionTokenIndexes, int(core.allOptionsSize),
|
||||
[](int i, const void *user) -> const Symbol * {
|
||||
return &(*reinterpret_cast<const decltype(reorderedOptions) *>(user))[i];
|
||||
},
|
||||
&reorderedOptions,
|
||||
[](const Symbol *s) {
|
||||
return static_cast<const Option *>(s)->token(); //
|
||||
},
|
||||
&maxWidth, reinterpret_cast<void *>(parserData->textProvider),
|
||||
parserData->textProvider(Strings::Title, Strings::Options));
|
||||
|
||||
Lists cmdLists = noHelp
|
||||
? Lists{nullptr, 0}
|
||||
: getLists(
|
||||
displayOptions, catalogueData->cmd.data, catalogueData->commands,
|
||||
core.cmdNameIndexes, int(d->commands.size()),
|
||||
[](int i, const void *user) -> const Symbol * {
|
||||
return &reinterpret_cast<decltype(d)>(user)->commands[i]; //
|
||||
},
|
||||
d,
|
||||
[](const Symbol *s) {
|
||||
return static_cast<const Command *>(s)->name(); //
|
||||
},
|
||||
&maxWidth, reinterpret_cast<void *>(parserData->textProvider),
|
||||
parserData->textProvider(Strings::Title, Strings::Commands));
|
||||
|
||||
// Get valid help layout
|
||||
const HelpLayoutPrivate *helpLayoutData = nullptr;
|
||||
{
|
||||
int cmdCnt = stack.size() + 1;
|
||||
auto cmds = new const Command *[cmdCnt]; // alloc
|
||||
auto cmd = &parserData->rootCommand;
|
||||
cmds[0] = cmd;
|
||||
int cmdIdx = 0;
|
||||
for (const auto &idx : std::as_const(stack)) {
|
||||
cmd = &cmd->d_func()->commands[idx];
|
||||
cmds[++cmdIdx] = cmd;
|
||||
}
|
||||
for (int i = cmdCnt - 1; i >= 0; --i) {
|
||||
auto curHelpLayoutData = cmds[i]->d_func()->helpLayout.d_func();
|
||||
if (!curHelpLayoutData->itemDataList.empty()) {
|
||||
helpLayoutData = curHelpLayoutData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete[] cmds; // free
|
||||
|
||||
if (!helpLayoutData) {
|
||||
static HelpLayout defaultHelpLayout = HelpLayout::defaultHelpLayout();
|
||||
helpLayoutData = defaultHelpLayout.d_func();
|
||||
}
|
||||
}
|
||||
if ((displayOptions & Parser::AlignAllCatalogues)) {
|
||||
for (const auto &helpItem : helpLayoutData->itemDataList) {
|
||||
if (helpItem.itemType != HelpLayoutPrivate::UserHelpList) {
|
||||
continue;
|
||||
}
|
||||
for (const auto &item : helpItem.list.firstColumn) {
|
||||
maxWidth = std::max(int(item.size()), maxWidth);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
maxWidth = 0;
|
||||
}
|
||||
|
||||
const auto &cmdDesc = d->detailedDescription.empty() ? d->desc : d->detailedDescription;
|
||||
|
||||
// Get last
|
||||
int last = int(helpLayoutData->itemDataList.size()) - 1;
|
||||
for (int i = last; i >= 0; --i) {
|
||||
const auto &item = helpLayoutData->itemDataList[i];
|
||||
|
||||
bool empty = true;
|
||||
switch (item.itemType) {
|
||||
case HelpLayoutPrivate::HelpText: {
|
||||
switch (static_cast<HelpLayout::HelpTextItem>(item.index)) {
|
||||
case HelpLayout::HT_Prologue: {
|
||||
if (noIntro)
|
||||
break;
|
||||
empty = parserData->prologue.empty();
|
||||
break;
|
||||
}
|
||||
case HelpLayout::HT_Epilogue: {
|
||||
if (noIntro)
|
||||
break;
|
||||
empty = parserData->epilogue.empty();
|
||||
break;
|
||||
}
|
||||
case HelpLayout::HT_Description: {
|
||||
if (noHelp)
|
||||
break;
|
||||
empty = cmdDesc.empty();
|
||||
break;
|
||||
}
|
||||
case HelpLayout::HT_Usage: {
|
||||
if (noHelp)
|
||||
break;
|
||||
empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HelpLayoutPrivate::HelpList: {
|
||||
if (noHelp)
|
||||
break;
|
||||
switch (static_cast<HelpLayout::HelpListItem>(item.index)) {
|
||||
case HelpLayout::HL_Arguments: {
|
||||
empty = argLists.size == 0;
|
||||
break;
|
||||
}
|
||||
case HelpLayout::HL_Options: {
|
||||
empty = optLists.size == 0;
|
||||
break;
|
||||
}
|
||||
case HelpLayout::HL_Commands: {
|
||||
empty = cmdLists.size == 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HelpLayoutPrivate::Message: {
|
||||
switch (static_cast<HelpLayout::MessageItem>(item.index)) {
|
||||
case HelpLayout::MI_Information: {
|
||||
empty = info.empty();
|
||||
break;
|
||||
}
|
||||
case HelpLayout::MI_Warning: {
|
||||
empty = warn.empty();
|
||||
break;
|
||||
}
|
||||
case HelpLayout::MI_Critical: {
|
||||
empty = err.empty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HelpLayoutPrivate::UserHelpText: {
|
||||
if (noHelp)
|
||||
break;
|
||||
empty = item.text.lines.empty();
|
||||
break;
|
||||
}
|
||||
case HelpLayoutPrivate::UserHelpList: {
|
||||
if (noHelp)
|
||||
break;
|
||||
empty = item.list.firstColumn.empty();
|
||||
break;
|
||||
}
|
||||
case HelpLayoutPrivate::UserIntro: {
|
||||
if (noIntro)
|
||||
break;
|
||||
empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty) {
|
||||
last = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Output
|
||||
for (int i = 0; i <= last; ++i) {
|
||||
const auto &item = helpLayoutData->itemDataList[i];
|
||||
HelpLayout::Context ctx;
|
||||
ctx.parser = &parser;
|
||||
bool hasNext = i < last;
|
||||
ctx.hasNext = hasNext;
|
||||
switch (item.itemType) {
|
||||
case HelpLayoutPrivate::HelpText: {
|
||||
HelpLayout::Text text;
|
||||
ctx.text = &text;
|
||||
switch (static_cast<HelpLayout::HelpTextItem>(item.index)) {
|
||||
case HelpLayout::HT_Prologue: {
|
||||
if (noIntro)
|
||||
break;
|
||||
text.lines = parserData->prologue;
|
||||
break;
|
||||
}
|
||||
case HelpLayout::HT_Epilogue: {
|
||||
if (noIntro)
|
||||
break;
|
||||
text.lines = parserData->epilogue;
|
||||
break;
|
||||
}
|
||||
case HelpLayout::HT_Description: {
|
||||
if (noHelp)
|
||||
break;
|
||||
text.title =
|
||||
parserData->textProvider(Strings::Title, Strings::Description);
|
||||
text.lines = cmdDesc;
|
||||
break;
|
||||
}
|
||||
case HelpLayout::HT_Usage: {
|
||||
if (noHelp)
|
||||
break;
|
||||
std::vector<Option> allOptions;
|
||||
allOptions.reserve(core.globalOptionsSize);
|
||||
for (int j = 0; j < core.globalOptionsSize; ++j) {
|
||||
allOptions.emplace_back(*core.allOptionsResult[j].option);
|
||||
}
|
||||
|
||||
{
|
||||
// get parent names
|
||||
auto p = &parserData->rootCommand;
|
||||
for (int index : stack) {
|
||||
text.lines = p->name() + " ";
|
||||
p = &p->d_func()->commands[index];
|
||||
}
|
||||
}
|
||||
|
||||
void *a[2] = {
|
||||
&allOptions,
|
||||
reinterpret_cast<void *>(parserData->textProvider),
|
||||
};
|
||||
text.title = parserData->textProvider(Strings::Title, Strings::Usage);
|
||||
text.lines += command->helpText(Symbol::HP_Usage, displayOptions, a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
item.out(ctx);
|
||||
break;
|
||||
}
|
||||
case HelpLayoutPrivate::HelpList: {
|
||||
if (noHelp)
|
||||
break;
|
||||
|
||||
const auto &listHasNext = [](int j, const Lists &lists) {
|
||||
return j < lists.size - 1;
|
||||
};
|
||||
|
||||
ctx.firstColumnLength = maxWidth;
|
||||
switch (static_cast<HelpLayout::HelpListItem>(item.index)) {
|
||||
case HelpLayout::HL_Arguments: {
|
||||
for (int j = 0; j < argLists.size; ++j) {
|
||||
ctx.hasNext = hasNext || listHasNext(j, argLists);
|
||||
ctx.list = &argLists.data[j];
|
||||
item.out(ctx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HelpLayout::HL_Options: {
|
||||
for (int j = 0; j < optLists.size; ++j) {
|
||||
ctx.hasNext = hasNext || listHasNext(j, optLists);
|
||||
ctx.list = &optLists.data[j];
|
||||
item.out(ctx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HelpLayout::HL_Commands: {
|
||||
for (int j = 0; j < cmdLists.size; ++j) {
|
||||
ctx.hasNext = hasNext || listHasNext(j, cmdLists);
|
||||
ctx.list = &cmdLists.data[j];
|
||||
item.out(ctx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HelpLayoutPrivate::Message: {
|
||||
HelpLayout::Text text;
|
||||
ctx.text = &text;
|
||||
switch (static_cast<HelpLayout::MessageItem>(item.index)) {
|
||||
case HelpLayout::MI_Information: {
|
||||
text.lines = info;
|
||||
break;
|
||||
}
|
||||
case HelpLayout::MI_Warning: {
|
||||
text.lines = warn;
|
||||
break;
|
||||
}
|
||||
case HelpLayout::MI_Critical: {
|
||||
text.lines = err;
|
||||
break;
|
||||
}
|
||||
}
|
||||
item.out(ctx);
|
||||
break;
|
||||
}
|
||||
case HelpLayoutPrivate::UserHelpText: {
|
||||
if (noHelp)
|
||||
break;
|
||||
ctx.text = &item.text;
|
||||
item.out(ctx);
|
||||
break;
|
||||
}
|
||||
case HelpLayoutPrivate::UserHelpList: {
|
||||
if (noHelp)
|
||||
break;
|
||||
ctx.list = &item.list;
|
||||
ctx.firstColumnLength = maxWidth;
|
||||
item.out(ctx);
|
||||
break;
|
||||
}
|
||||
case HelpLayoutPrivate::UserIntro: {
|
||||
if (noHelp)
|
||||
break;
|
||||
item.out(ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Free
|
||||
delete[] argLists.data;
|
||||
delete[] optLists.data;
|
||||
delete[] cmdLists.data;
|
||||
}
|
||||
|
||||
ParseResult::ParseResult() : SharedBase(nullptr) {
|
||||
}
|
||||
|
||||
Command ParseResult::rootCommand() const {
|
||||
Q_D2(ParseResult);
|
||||
return d->parser.rootCommand();
|
||||
}
|
||||
|
||||
const std::vector<std::string> &ParseResult::arguments() const {
|
||||
Q_D2(ParseResult);
|
||||
return d->arguments;
|
||||
}
|
||||
|
||||
int ParseResult::invoke(int errCode) const {
|
||||
Q_D2(ParseResult);
|
||||
if (d->error != NoError) {
|
||||
showError();
|
||||
return errCode;
|
||||
}
|
||||
return dispatch();
|
||||
}
|
||||
|
||||
int ParseResult::dispatch() const {
|
||||
Q_D2(ParseResult);
|
||||
if (d->error != NoError) {
|
||||
throw std::runtime_error("don't call dispatch when parsing fails");
|
||||
}
|
||||
|
||||
const auto &cmdData = d->command->d_func();
|
||||
const auto &handler = cmdData->handler;
|
||||
|
||||
// If version is empty, you should do something in the handler
|
||||
if (d->roleSet[Option::Version] && !cmdData->version.empty()) {
|
||||
u8info("%s\n", cmdData->version.data());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (d->roleSet[Option::Help]) {
|
||||
showHelpText();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!handler) {
|
||||
throw std::runtime_error(
|
||||
Utils::formatText(R"(command "%1" doesn't have a valid handler)", {
|
||||
cmdData->name,
|
||||
}));
|
||||
}
|
||||
|
||||
return handler(*this);
|
||||
}
|
||||
|
||||
ParseResult::Error ParseResult::error() const {
|
||||
Q_D2(ParseResult);
|
||||
return d->error;
|
||||
}
|
||||
|
||||
std::string ParseResult::errorText() const {
|
||||
Q_D2(ParseResult);
|
||||
if (d->error == NoError)
|
||||
return {};
|
||||
return Utils::formatText(d->parser.d_func()->textProvider(Strings::ParseError, d->error),
|
||||
d->errorPlaceholders);
|
||||
}
|
||||
|
||||
std::string ParseResult::correctionText() const {
|
||||
Q_D2(ParseResult);
|
||||
return d->correctionText();
|
||||
}
|
||||
|
||||
std::string ParseResult::cancellationToken() const {
|
||||
Q_D2(ParseResult);
|
||||
return d->cancellationToken;
|
||||
}
|
||||
|
||||
Command ParseResult::command() const {
|
||||
Q_D2(ParseResult);
|
||||
return *d->command;
|
||||
}
|
||||
|
||||
std::vector<Option> ParseResult::globalOptions() const {
|
||||
Q_D2(ParseResult);
|
||||
return d->globalOptions();
|
||||
}
|
||||
|
||||
std::vector<int> ParseResult::commandIndexStack() const {
|
||||
Q_D2(ParseResult);
|
||||
return d->stack;
|
||||
}
|
||||
|
||||
int ParseResult::indexOfArgument(const std::string &name) const {
|
||||
Q_D2(ParseResult);
|
||||
auto it = d->core.argNameIndexes.find(name);
|
||||
if (it == d->core.argNameIndexes.end())
|
||||
return -1;
|
||||
return it->second.i;
|
||||
}
|
||||
|
||||
int ParseResult::indexOfOption(const std::string &token) const {
|
||||
Q_D2(ParseResult);
|
||||
auto it = d->core.allOptionTokenIndexes.find(token);
|
||||
if (it == d->core.allOptionTokenIndexes.end())
|
||||
return -1;
|
||||
return it->second.i;
|
||||
}
|
||||
|
||||
void ParseResult::showError() const {
|
||||
Q_D2(ParseResult);
|
||||
if (d->error == NoError)
|
||||
return;
|
||||
|
||||
const auto &parserData = d->parser.d_func();
|
||||
const auto &displayOptions = parserData->displayOptions;
|
||||
d->showMessage(
|
||||
(displayOptions & Parser::SkipCorrection) ? std::string() : d->correctionText(), {},
|
||||
parserData->textProvider(Strings::Title, Strings::Error) + ": " + errorText(), true);
|
||||
}
|
||||
|
||||
void ParseResult::showHelpText() const {
|
||||
Q_D2(ParseResult);
|
||||
d->showMessage({}, {}, {});
|
||||
}
|
||||
|
||||
void ParseResult::showMessage(const std::string &info, const std::string &warn,
|
||||
const std::string &err) const {
|
||||
Q_D2(ParseResult);
|
||||
const auto &displayOptions = d->parser.d_func()->displayOptions;
|
||||
d->showMessage(info, warn, err, true);
|
||||
}
|
||||
|
||||
bool ParseResult::isRoleSet(Option::Role role) const {
|
||||
Q_D2(ParseResult);
|
||||
return d->roleSet[role];
|
||||
}
|
||||
|
||||
const std::vector<Value> &ParseResult::values(int index) const {
|
||||
Q_D2(ParseResult);
|
||||
if (index < 0 || index >= d->core.argSize)
|
||||
return sharedEmptyValues();
|
||||
return d->core.argResult[index];
|
||||
}
|
||||
|
||||
Value ParseResult::value(int index) const {
|
||||
Q_D2(ParseResult);
|
||||
if (index < 0 || index >= d->core.argSize)
|
||||
return {};
|
||||
const auto &args = d->core.argResult[index];
|
||||
return args.empty() ? d->command->d_func()->arguments[index].defaultValue() : args.front();
|
||||
}
|
||||
|
||||
OptionResult ParseResult::option(int index) const {
|
||||
Q_D2(ParseResult);
|
||||
if (index < 0 || index >= d->core.allOptionsSize)
|
||||
return {};
|
||||
return {&d->core.allOptionsResult[index]};
|
||||
}
|
||||
|
||||
ParseResult::ParseResult(ParseResultPrivate *d) : SharedBase(d) {
|
||||
}
|
||||
|
||||
}
|
||||
100
libs/qwindowkit/qmsetup/src/syscmdline/src/parseresult_p.h
Normal file
100
libs/qwindowkit/qmsetup/src/syscmdline/src/parseresult_p.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef PARSERESULT_P_H
|
||||
#define PARSERESULT_P_H
|
||||
|
||||
#include "sharedbase_p.h"
|
||||
#include "parser.h"
|
||||
|
||||
#include "map_p.h"
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
struct ArgumentHolderData {
|
||||
int optionalArgIndex;
|
||||
int multiValueArgIndex;
|
||||
GenericMap argNameIndexes; // name -> index of argument
|
||||
int argSize; // equal to `argumentCount()`
|
||||
|
||||
ArgumentHolderData() : optionalArgIndex(-1), multiValueArgIndex(-1), argSize(0) {
|
||||
}
|
||||
};
|
||||
|
||||
struct OptionData : public ArgumentHolderData {
|
||||
const Option *option; // MUST BE SET
|
||||
std::vector<Value> **argResult; // first: occurrence, second: arg index
|
||||
int count; // occurrence times
|
||||
|
||||
OptionData() : option(nullptr), argResult(nullptr), count(0) {
|
||||
}
|
||||
|
||||
~OptionData() {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
delete[] argResult[i];
|
||||
}
|
||||
delete[] argResult;
|
||||
}
|
||||
|
||||
static const OptionData &sharedNull();
|
||||
};
|
||||
|
||||
struct ParseResultData2 : public ArgumentHolderData {
|
||||
std::vector<Value> *argResult; // arg result
|
||||
|
||||
OptionData *allOptionsResult; // option result
|
||||
int allOptionsSize; // command option count + global option count
|
||||
int globalOptionsSize; // global option count
|
||||
GenericMap allOptionTokenIndexes; // token -> index of `allOptionsResult`
|
||||
|
||||
GenericMap cmdNameIndexes; // name -> index of command
|
||||
|
||||
~ParseResultData2() {
|
||||
delete[] allOptionsResult;
|
||||
delete[] argResult;
|
||||
}
|
||||
};
|
||||
|
||||
class ParseResultPrivate : public SharedBasePrivate {
|
||||
public:
|
||||
ParseResultPrivate()
|
||||
: error(ParseResult::NoError), errorOption(nullptr), errorArgument(nullptr) {
|
||||
}
|
||||
|
||||
ParseResultPrivate(const ParseResultPrivate &) = delete;
|
||||
ParseResultPrivate &operator=(const ParseResultPrivate &) = delete;
|
||||
|
||||
// This is a read-only class, we can store the pointers pointing to
|
||||
// its own data, and we don't need to implement the clone method
|
||||
SharedBasePrivate *clone() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// parse information
|
||||
Parser parser;
|
||||
std::vector<std::string> arguments;
|
||||
|
||||
// error related
|
||||
ParseResult::Error error;
|
||||
std::vector<std::string> errorPlaceholders;
|
||||
const Option *errorOption;
|
||||
const Argument *errorArgument;
|
||||
std::string cancellationToken;
|
||||
|
||||
// success results
|
||||
std::vector<int> stack;
|
||||
const Command *command; // MUST BE SET
|
||||
|
||||
ParseResultData2 core;
|
||||
|
||||
bool roleSet[5] = {};
|
||||
|
||||
std::string correctionText() const;
|
||||
|
||||
std::vector<Option> globalOptions() const;
|
||||
|
||||
// isMsg: mainly to display message rather than help
|
||||
void showMessage(const std::string &info, const std::string &catalogNames,
|
||||
const std::string &symbolIndexes, bool isMsg = false) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // PARSERESULT_P_H
|
||||
50
libs/qwindowkit/qmsetup/src/syscmdline/src/sharedbase.cpp
Normal file
50
libs/qwindowkit/qmsetup/src/syscmdline/src/sharedbase.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "sharedbase.h"
|
||||
#include "sharedbase_p.h"
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
// static int g_cnt;
|
||||
|
||||
SharedBasePrivate::SharedBasePrivate() : ref(1) {
|
||||
// printf("construct %d\n", ++g_cnt);
|
||||
}
|
||||
|
||||
SharedBasePrivate::~SharedBasePrivate() {
|
||||
// printf("destruct %d\n", --g_cnt);
|
||||
}
|
||||
|
||||
SharedBase::~SharedBase() {
|
||||
ref_deref(d_ptr);
|
||||
}
|
||||
|
||||
SharedBase::SharedBase(const SharedBase &other) : d_ptr(other.d_ptr) {
|
||||
ref_ref(d_ptr);
|
||||
}
|
||||
|
||||
SharedBase &SharedBase::operator=(const SharedBase &other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
ref_assign(d_ptr, other.d_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool SharedBase::isDetached() const {
|
||||
Q_D2(SharedBase);
|
||||
return d && d->ref.load() == 1;
|
||||
}
|
||||
|
||||
void SharedBase::detach() {
|
||||
auto &d = d_ptr;
|
||||
if (d->ref.load() != 1) {
|
||||
auto x = d->clone();
|
||||
if (d->ref.fetch_sub(1) == 1) // atomicity
|
||||
delete d;
|
||||
d = x;
|
||||
}
|
||||
}
|
||||
|
||||
SharedBase::SharedBase(SharedBasePrivate *d) : d_ptr(d) {
|
||||
}
|
||||
|
||||
}
|
||||
51
libs/qwindowkit/qmsetup/src/syscmdline/src/sharedbase_p.h
Normal file
51
libs/qwindowkit/qmsetup/src/syscmdline/src/sharedbase_p.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef SHAREDBASE_P_H
|
||||
#define SHAREDBASE_P_H
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class SharedBasePrivate {
|
||||
public:
|
||||
SharedBasePrivate();
|
||||
virtual ~SharedBasePrivate();
|
||||
|
||||
virtual SharedBasePrivate *clone() const = 0;
|
||||
|
||||
// Declare basic copy constructor
|
||||
SharedBasePrivate(const SharedBasePrivate &) : ref(0) {
|
||||
}
|
||||
|
||||
SharedBasePrivate &operator=(const SharedBasePrivate &) = delete;
|
||||
|
||||
std::atomic_int ref;
|
||||
};
|
||||
|
||||
inline void ref_ref(SharedBasePrivate *d) {
|
||||
if (d)
|
||||
d->ref.fetch_add(1);
|
||||
}
|
||||
|
||||
inline void ref_deref(SharedBasePrivate *d) {
|
||||
if (d && d->ref.fetch_sub(1) == 1)
|
||||
delete d;
|
||||
}
|
||||
|
||||
inline void ref_assign(SharedBasePrivate *&d, SharedBasePrivate *d2) {
|
||||
if (d2)
|
||||
d2->ref.fetch_add(1);
|
||||
if (d && d->ref.fetch_sub(1) == 1)
|
||||
delete d;
|
||||
d = d2;
|
||||
}
|
||||
|
||||
#define Q_D(T) \
|
||||
detach(); \
|
||||
T##Private *const d = reinterpret_cast<T##Private *>(d_ptr)
|
||||
|
||||
#define Q_D2(T) \
|
||||
const T##Private *const d = reinterpret_cast<const T##Private *>(d_ptr)
|
||||
|
||||
}
|
||||
|
||||
#endif // SHAREDBASE_P_H
|
||||
65
libs/qwindowkit/qmsetup/src/syscmdline/src/symbol.cpp
Normal file
65
libs/qwindowkit/qmsetup/src/syscmdline/src/symbol.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "symbol.h"
|
||||
#include "symbol_p.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
SymbolPrivate::SymbolPrivate(Symbol::SymbolType type, std::string desc)
|
||||
: type(type), desc(std::move(desc)) {
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the symbol type.
|
||||
*/
|
||||
Symbol::SymbolType Symbol::type() const {
|
||||
Q_D2(Symbol);
|
||||
return d->type;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the symbol description.
|
||||
*/
|
||||
std::string Symbol::description() const {
|
||||
Q_D2(Symbol);
|
||||
return d->desc;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the symbol description.
|
||||
*/
|
||||
void Symbol::setDescription(const std::string &desc) {
|
||||
Q_D(Symbol);
|
||||
d->desc = desc;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the help provider.
|
||||
*/
|
||||
Symbol::HelpProvider Symbol::helpProvider() const {
|
||||
Q_D2(Symbol);
|
||||
return d->helpProvider;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the help provider.
|
||||
*/
|
||||
void Symbol::setHelpProvider(const Symbol::HelpProvider &helpProvider) {
|
||||
Q_D(Symbol);
|
||||
d->helpProvider = helpProvider;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the help text at given position with given display options.
|
||||
*/
|
||||
std::string Symbol::helpText(Symbol::HelpPosition pos, int displayOptions, void *extra) const {
|
||||
Q_D2(Symbol);
|
||||
if (d->helpProvider)
|
||||
return d->helpProvider(this, pos, displayOptions, extra);
|
||||
return {};
|
||||
}
|
||||
|
||||
Symbol::Symbol(SymbolPrivate *d) : SharedBase(d) {
|
||||
}
|
||||
|
||||
}
|
||||
23
libs/qwindowkit/qmsetup/src/syscmdline/src/symbol_p.h
Normal file
23
libs/qwindowkit/qmsetup/src/syscmdline/src/symbol_p.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef SYMBOL_P_H
|
||||
#define SYMBOL_P_H
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "symbol.h"
|
||||
#include "sharedbase_p.h"
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
class SymbolPrivate : public SharedBasePrivate {
|
||||
public:
|
||||
SymbolPrivate(Symbol::SymbolType type, std::string desc);
|
||||
|
||||
public:
|
||||
Symbol::SymbolType type;
|
||||
std::string desc;
|
||||
Symbol::HelpProvider helpProvider;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SYMBOL_P_H
|
||||
520
libs/qwindowkit/qmsetup/src/syscmdline/src/system.cpp
Normal file
520
libs/qwindowkit/qmsetup/src/syscmdline/src/system.cpp
Normal file
@@ -0,0 +1,520 @@
|
||||
#include "system.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <cstring>
|
||||
#include <cstdarg>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include <shellapi.h>
|
||||
|
||||
# include "utils_p.h"
|
||||
#else
|
||||
# include <limits.h>
|
||||
# include <sys/stat.h>
|
||||
# include <utime.h>
|
||||
# ifdef __APPLE__
|
||||
# include <crt_externs.h>
|
||||
# include <mach-o/dyld.h>
|
||||
# else
|
||||
# include <fstream>
|
||||
# endif
|
||||
# include <filesystem>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifndef WC_ERR_INVALID_CHARS
|
||||
# define WC_ERR_INVALID_CHARS 0x00000080
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::wstring winGetFullModuleFileName(HMODULE hModule) {
|
||||
// Use stack buffer for the first try
|
||||
wchar_t stackBuf[MAX_PATH + 1];
|
||||
|
||||
// Call
|
||||
wchar_t *buf = stackBuf;
|
||||
auto size = ::GetModuleFileNameW(nullptr, buf, MAX_PATH);
|
||||
if (size == 0) {
|
||||
return {};
|
||||
}
|
||||
if (size > MAX_PATH) {
|
||||
// Re-alloc
|
||||
buf = new wchar_t[size + 1]; // The return size doesn't contain the terminating 0
|
||||
|
||||
// Call
|
||||
if (::GetModuleFileNameW(nullptr, buf, size) == 0) {
|
||||
delete[] buf;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::replace(buf, buf + size, L'\\', L'/');
|
||||
|
||||
// Return
|
||||
std::wstring res(buf);
|
||||
if (buf != stackBuf) {
|
||||
delete[] buf;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
static std::string macGetExecutablePath() {
|
||||
// Use stack buffer for the first try
|
||||
char stackBuf[PATH_MAX + 1];
|
||||
|
||||
// "_NSGetExecutablePath" will return "-1" if the buffer is not large enough
|
||||
// and "*bufferSize" will be set to the size required.
|
||||
|
||||
// Call
|
||||
unsigned int size = PATH_MAX + 1;
|
||||
char *buf = stackBuf;
|
||||
if (_NSGetExecutablePath(buf, &size) != 0) {
|
||||
// Re-alloc
|
||||
buf = new char[size]; // The return size contains the terminating 0
|
||||
|
||||
// Call
|
||||
if (_NSGetExecutablePath(buf, &size) != 0) {
|
||||
delete[] buf;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Return
|
||||
std::string res(buf);
|
||||
if (buf != stackBuf) {
|
||||
delete[] buf;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
Returns the wide string converted from UTF-8 multi-byte string.
|
||||
*/
|
||||
std::string wideToUtf8(const std::wstring &s) {
|
||||
#ifdef _WIN32
|
||||
if (s.empty()) {
|
||||
return {};
|
||||
}
|
||||
const auto utf16Length = static_cast<int>(s.size());
|
||||
if (utf16Length >= (std::numeric_limits<int>::max)()) {
|
||||
return {};
|
||||
}
|
||||
const int utf8Length = ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s.data(),
|
||||
utf16Length, nullptr, 0, nullptr, nullptr);
|
||||
if (utf8Length <= 0) {
|
||||
return {};
|
||||
}
|
||||
std::string utf8Str;
|
||||
utf8Str.resize(utf8Length);
|
||||
::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s.data(), utf16Length, utf8Str.data(),
|
||||
utf8Length, nullptr, nullptr);
|
||||
return utf8Str;
|
||||
#else
|
||||
return std::filesystem::path(s).string();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the UTF-8 multi-byte string converted from wide string.
|
||||
*/
|
||||
std::wstring utf8ToWide(const std::string &s) {
|
||||
#ifdef _WIN32
|
||||
if (s.empty()) {
|
||||
return {};
|
||||
}
|
||||
const auto utf8Length = static_cast<int>(s.size());
|
||||
if (utf8Length >= (std::numeric_limits<int>::max)()) {
|
||||
return {};
|
||||
}
|
||||
const int utf16Length =
|
||||
::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), utf8Length, nullptr, 0);
|
||||
if (utf16Length <= 0) {
|
||||
return {};
|
||||
}
|
||||
std::wstring utf16Str;
|
||||
utf16Str.resize(utf16Length);
|
||||
::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), utf8Length, utf16Str.data(),
|
||||
utf16Length);
|
||||
return utf16Str;
|
||||
#else
|
||||
return std::filesystem::path(s).wstring();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
/*!
|
||||
Returns the UTF-8 multi-byte string converted from ANSI string.
|
||||
*/
|
||||
std::string ansiToUtf8(const std::string &s) {
|
||||
if (s.empty()) {
|
||||
return {};
|
||||
}
|
||||
const auto ansiLength = static_cast<int>(s.size());
|
||||
if (ansiLength >= (std::numeric_limits<int>::max)()) {
|
||||
return {};
|
||||
}
|
||||
const int utf16Length =
|
||||
::MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, s.data(), ansiLength, nullptr, 0);
|
||||
if (utf16Length <= 0) {
|
||||
return {};
|
||||
}
|
||||
std::wstring utf16Str;
|
||||
utf16Str.resize(utf16Length);
|
||||
::MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, s.data(), ansiLength, utf16Str.data(),
|
||||
utf16Length);
|
||||
return wideToUtf8(utf16Str);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
Returns the application file path, the path separator of which is <tt>/</tt>.
|
||||
*/
|
||||
std::string appPath() {
|
||||
static const auto res = []() -> std::string {
|
||||
#ifdef _WIN32
|
||||
return wideToUtf8(winGetFullModuleFileName(nullptr));
|
||||
#elif defined(__APPLE__)
|
||||
return macGetExecutablePath();
|
||||
#else
|
||||
char buf[PATH_MAX];
|
||||
if (!realpath("/proc/self/exe", buf)) {
|
||||
return {};
|
||||
}
|
||||
return buf;
|
||||
#endif
|
||||
}();
|
||||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the application directory path, the path separator of which is <tt>/</tt>.
|
||||
*/
|
||||
std::string appDirectory() {
|
||||
auto appDir = appPath();
|
||||
auto slashIdx = appDir.find_last_of('/');
|
||||
if (slashIdx != std::string::npos) {
|
||||
appDir = appDir.substr(0, slashIdx);
|
||||
}
|
||||
return appDir;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the application file name.
|
||||
*/
|
||||
std::string appFileName() {
|
||||
auto appName = appPath();
|
||||
auto slashIdx = appName.find_last_of('/');
|
||||
if (slashIdx != std::string::npos) {
|
||||
appName = appName.substr(slashIdx + 1);
|
||||
}
|
||||
return appName;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the application name. On Windows, the file extension will be stripped.
|
||||
*/
|
||||
std::string appName() {
|
||||
auto appName = appFileName();
|
||||
#ifdef _WIN32
|
||||
auto dotIdx = appName.find_last_of('.');
|
||||
if (dotIdx != std::string::npos) {
|
||||
std::string suffix = Utils::toLower(appName.substr(dotIdx + 1));
|
||||
if (suffix == "exe") {
|
||||
appName = appName.substr(0, dotIdx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return appName;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the command line argument list in UTF-8.
|
||||
*/
|
||||
std::vector<std::string> commandLineArguments() {
|
||||
std::vector<std::string> res;
|
||||
#ifdef _WIN32
|
||||
int argc;
|
||||
auto argvW = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
|
||||
if (argvW == nullptr)
|
||||
return {};
|
||||
res.reserve(argc);
|
||||
for (int i = 0; i != argc; ++i) {
|
||||
res.push_back(wideToUtf8(argvW[i]));
|
||||
}
|
||||
::LocalFree(argvW);
|
||||
#elif defined(__APPLE__)
|
||||
auto argv = *(_NSGetArgv());
|
||||
for (int i = 0; argv[i] != nullptr; ++i) {
|
||||
res.push_back(argv[i]);
|
||||
}
|
||||
#else
|
||||
std::ifstream file("/proc/self/cmdline", std::ios::in);
|
||||
if (file.fail())
|
||||
return {};
|
||||
std::string s;
|
||||
while (std::getline(file, s, '\0')) {
|
||||
res.push_back(s);
|
||||
}
|
||||
file.close();
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
class PrintScopeGuard {
|
||||
public:
|
||||
static std::mutex &global_mtx() {
|
||||
static std::mutex _instance;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
explicit PrintScopeGuard(int foreground, int background)
|
||||
: consoleChanged(!(foreground == DefaultColor && background == DefaultColor)) {
|
||||
global_mtx().lock();
|
||||
#ifdef _WIN32
|
||||
_codepage = ::GetConsoleOutputCP();
|
||||
::SetConsoleOutputCP(CP_UTF8);
|
||||
|
||||
if (consoleChanged) {
|
||||
WORD winColor = 0;
|
||||
if (foreground != DefaultColor) {
|
||||
winColor |= (foreground & Intensified) ? FOREGROUND_INTENSITY : 0;
|
||||
switch (foreground & 0xF) {
|
||||
case Red:
|
||||
winColor |= FOREGROUND_RED;
|
||||
break;
|
||||
case Green:
|
||||
winColor |= FOREGROUND_GREEN;
|
||||
break;
|
||||
case Blue:
|
||||
winColor |= FOREGROUND_BLUE;
|
||||
break;
|
||||
case Yellow:
|
||||
winColor |= FOREGROUND_RED | FOREGROUND_GREEN;
|
||||
break;
|
||||
case Purple:
|
||||
winColor |= FOREGROUND_RED | FOREGROUND_BLUE;
|
||||
break;
|
||||
case Cyan:
|
||||
winColor |= FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
break;
|
||||
case White:
|
||||
winColor |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (background != DefaultColor) {
|
||||
winColor |= (background & Intensified) ? BACKGROUND_INTENSITY : 0;
|
||||
switch (background & 0xF) {
|
||||
case Red:
|
||||
winColor |= BACKGROUND_RED;
|
||||
break;
|
||||
case Green:
|
||||
winColor |= BACKGROUND_GREEN;
|
||||
break;
|
||||
case Blue:
|
||||
winColor |= BACKGROUND_BLUE;
|
||||
break;
|
||||
case Yellow:
|
||||
winColor |= BACKGROUND_RED | BACKGROUND_GREEN;
|
||||
break;
|
||||
case Purple:
|
||||
winColor |= BACKGROUND_RED | BACKGROUND_BLUE;
|
||||
break;
|
||||
case Cyan:
|
||||
winColor |= BACKGROUND_GREEN | BACKGROUND_BLUE;
|
||||
break;
|
||||
case White:
|
||||
winColor |= BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
GetConsoleScreenBufferInfo(_hConsole, &_csbi);
|
||||
SetConsoleTextAttribute(_hConsole, winColor);
|
||||
}
|
||||
#else
|
||||
if (consoleChanged) {
|
||||
const char *strList[3];
|
||||
int strListSize = 0;
|
||||
if (foreground != DefaultColor) {
|
||||
bool light = foreground & Intensified;
|
||||
const char *colorStr = nullptr;
|
||||
switch (foreground & 0xF) {
|
||||
case Red:
|
||||
colorStr = light ? "91" : "31";
|
||||
break;
|
||||
case Green:
|
||||
colorStr = light ? "92" : "32";
|
||||
break;
|
||||
case Blue:
|
||||
colorStr = light ? "94" : "34";
|
||||
break;
|
||||
case Yellow:
|
||||
colorStr = light ? "93" : "33";
|
||||
break;
|
||||
case Purple:
|
||||
colorStr = light ? "95" : "35";
|
||||
break;
|
||||
case Cyan:
|
||||
colorStr = light ? "96" : "36";
|
||||
break;
|
||||
case White:
|
||||
colorStr = light ? "97" : "37";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (colorStr) {
|
||||
strList[strListSize] = colorStr;
|
||||
strListSize++;
|
||||
}
|
||||
}
|
||||
if (background != DefaultColor) {
|
||||
bool light = background & Intensified;
|
||||
const char *colorStr = nullptr;
|
||||
switch (background & 0xF) {
|
||||
case Red:
|
||||
colorStr = light ? "101" : "41";
|
||||
break;
|
||||
case Green:
|
||||
colorStr = light ? "102" : "42";
|
||||
break;
|
||||
case Blue:
|
||||
colorStr = light ? "104" : "44";
|
||||
break;
|
||||
case Yellow:
|
||||
colorStr = light ? "103" : "43";
|
||||
break;
|
||||
case Purple:
|
||||
colorStr = light ? "105" : "45";
|
||||
break;
|
||||
case Cyan:
|
||||
colorStr = light ? "106" : "46";
|
||||
break;
|
||||
case White:
|
||||
colorStr = light ? "107" : "47";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (colorStr) {
|
||||
strList[strListSize] = colorStr;
|
||||
strListSize++;
|
||||
}
|
||||
}
|
||||
if (strListSize > 0) {
|
||||
char buf[20];
|
||||
int bufSize = 0;
|
||||
auto buf_puts = [&buf, &bufSize](const char *s) {
|
||||
auto len = strlen(s);
|
||||
for (; *s != '\0'; ++s) {
|
||||
buf[bufSize++] = *s;
|
||||
}
|
||||
};
|
||||
buf_puts("\033[");
|
||||
for (int i = 0; i < strListSize - 1; ++i) {
|
||||
buf_puts(strList[i]);
|
||||
buf_puts(";");
|
||||
}
|
||||
buf_puts(strList[strListSize - 1]);
|
||||
buf_puts("m");
|
||||
buf[bufSize] = '\0';
|
||||
printf("%s", buf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
~PrintScopeGuard() {
|
||||
#ifdef _WIN32
|
||||
::SetConsoleOutputCP(_codepage);
|
||||
|
||||
if (consoleChanged) {
|
||||
SetConsoleTextAttribute(_hConsole, _csbi.wAttributes);
|
||||
}
|
||||
#else
|
||||
if (consoleChanged) {
|
||||
// ANSI escape code to reset text color to default
|
||||
const char *resetColor = "\033[0m";
|
||||
printf("%s", resetColor);
|
||||
}
|
||||
#endif
|
||||
global_mtx().unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
bool consoleChanged;
|
||||
#ifdef _WIN32
|
||||
UINT _codepage;
|
||||
HANDLE _hConsole;
|
||||
CONSOLE_SCREEN_BUFFER_INFO _csbi;
|
||||
#endif
|
||||
};
|
||||
|
||||
int u8printf(int foreground, int background, const char *fmt, ...) {
|
||||
PrintScopeGuard _guard(foreground, background);
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int res = std::vprintf(fmt, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
|
||||
int u8vprintf(int foreground, int background, const char *fmt, va_list args) {
|
||||
PrintScopeGuard _guard(foreground, background);
|
||||
return std::vprintf(fmt, args);
|
||||
}
|
||||
|
||||
/*!
|
||||
Prints the formatted text in UTF-8 with given message type.
|
||||
*/
|
||||
int u8debug(MessageType messageType, bool highlight, const char *fmt, ...) {
|
||||
int color;
|
||||
switch (messageType) {
|
||||
case MT_Message:
|
||||
color = White;
|
||||
break;
|
||||
case MT_Healthy:
|
||||
color = Green;
|
||||
break;
|
||||
case MT_Warning:
|
||||
color = Yellow;
|
||||
break;
|
||||
case MT_Critical:
|
||||
color = Red;
|
||||
break;
|
||||
default:
|
||||
color = DefaultColor;
|
||||
break;
|
||||
}
|
||||
if (highlight) {
|
||||
color |= Intensified;
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int res = u8vprintf(color, DefaultColor, fmt, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int u8info(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int res = u8vprintf(DefaultColor, DefaultColor, fmt, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
104
libs/qwindowkit/qmsetup/src/syscmdline/src/utils.cpp
Normal file
104
libs/qwindowkit/qmsetup/src/syscmdline/src/utils.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include "utils_p.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace SysCmdLine::Utils {
|
||||
|
||||
std::vector<std::string> split(const std::string &s, const std::string &delimiter) {
|
||||
std::vector<std::string> tokens;
|
||||
std::string::size_type start = 0;
|
||||
std::string::size_type end = s.find(delimiter);
|
||||
while (end != std::string::npos) {
|
||||
tokens.push_back(s.substr(start, end - start));
|
||||
start = end + delimiter.size();
|
||||
end = s.find(delimiter, start);
|
||||
}
|
||||
tokens.push_back(s.substr(start));
|
||||
return tokens;
|
||||
}
|
||||
|
||||
std::string join(const std::vector<std::string> &v, const std::string &delimiter) {
|
||||
if (v.empty())
|
||||
return {};
|
||||
|
||||
std::string res;
|
||||
for (int i = 0; i < v.size() - 1; ++i) {
|
||||
res.append(v[i]);
|
||||
res.append(delimiter);
|
||||
}
|
||||
res.append(v.back());
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string formatText(const std::string &format, const std::vector<std::string> &args) {
|
||||
std::string result = format;
|
||||
for (size_t i = 0; i < args.size(); i++) {
|
||||
std::string placeholder = "%" + std::to_string(i + 1);
|
||||
size_t pos = result.find(placeholder);
|
||||
while (pos != std::string::npos) {
|
||||
result.replace(pos, placeholder.length(), args[i]);
|
||||
pos = result.find(placeholder, pos + args[i].size());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int levenshteinDistance(const std::string &s1, const std::string &s2) {
|
||||
auto len1 = s1.size();
|
||||
auto len2 = s2.size();
|
||||
|
||||
// Alloc
|
||||
int **dp = new int *[len1 + 1];
|
||||
for (int i = 0; i <= len1; i++) {
|
||||
dp[i] = new int[len2 + 1];
|
||||
}
|
||||
|
||||
for (int i = 0; i <= len1; i++) {
|
||||
for (int j = 0; j <= len2; j++) {
|
||||
if (i == 0) {
|
||||
dp[i][j] = j;
|
||||
} else if (j == 0) {
|
||||
dp[i][j] = i;
|
||||
} else if (s1[i - 1] == s2[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
dp[i][j] = 1 + std::min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int res = dp[len1][len2];
|
||||
|
||||
// Free
|
||||
for (int i = 0; i <= len1; i++) {
|
||||
delete[] dp[i];
|
||||
}
|
||||
delete[] dp;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::string> calcClosestTexts(const std::vector<std::string> &texts,
|
||||
const std::string &input, int threshold) {
|
||||
|
||||
std::vector<std::string> suggestions;
|
||||
for (const auto &cmd : texts) {
|
||||
int distance = levenshteinDistance(input, cmd);
|
||||
if (distance <= threshold) {
|
||||
suggestions.push_back(cmd);
|
||||
}
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
std::string toUpper(std::string s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string toLower(std::string s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
69
libs/qwindowkit/qmsetup/src/syscmdline/src/utils_p.h
Normal file
69
libs/qwindowkit/qmsetup/src/syscmdline/src/utils_p.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace SysCmdLine::Utils {
|
||||
|
||||
std::vector<std::string> split(const std::string &s, const std::string &delimiter);
|
||||
|
||||
std::string join(const std::vector<std::string> &v, const std::string &delimiter);
|
||||
|
||||
std::string formatText(const std::string &format, const std::vector<std::string> &args);
|
||||
|
||||
std::vector<std::string> calcClosestTexts(const std::vector<std::string> &texts,
|
||||
const std::string &input, int threshold);
|
||||
|
||||
std::string toUpper(std::string s);
|
||||
|
||||
std::string toLower(std::string s);
|
||||
|
||||
inline bool starts_with(const std::string_view &s, const std::string_view &prefix) {
|
||||
#if __cplusplus >= 202002L
|
||||
return s.starts_with(prefix);
|
||||
#else
|
||||
return s.size() >= prefix.size() && s.substr(0, prefix.size()) == prefix;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class Container, class T>
|
||||
inline bool contains(const Container &container, const T &key) {
|
||||
#if __cplusplus >= 202002L
|
||||
return container.contains(key);
|
||||
#else
|
||||
return container.count(key) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::vector<T> concatVector(const std::vector<T> &v1, const std::vector<T> &v2) {
|
||||
std::vector<T> res;
|
||||
res.reserve(v1.size() + v2.size());
|
||||
for (const auto &item : v1)
|
||||
res.emplace_back(item);
|
||||
for (const auto &item : v2)
|
||||
res.emplace_back(item);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::basic_string<T> trim(const std::basic_string<T> &str) {
|
||||
auto start = str.begin();
|
||||
while (start != str.end() && std::isspace(*start)) {
|
||||
start++;
|
||||
}
|
||||
|
||||
auto end = str.end();
|
||||
do {
|
||||
end--;
|
||||
} while (std::distance(start, end) > 0 && std::isspace(*end));
|
||||
|
||||
return {start, end + 1};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // UTILS_H
|
||||
360
libs/qwindowkit/qmsetup/src/syscmdline/src/value.cpp
Normal file
360
libs/qwindowkit/qmsetup/src/syscmdline/src/value.cpp
Normal file
@@ -0,0 +1,360 @@
|
||||
#include "value.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "utils_p.h"
|
||||
|
||||
namespace SysCmdLine {
|
||||
|
||||
static constexpr const char True_Literal[] = "true";
|
||||
static constexpr const char False_Literal[] = "false";
|
||||
|
||||
Value::Value(Value::Type type) : _type(type) {
|
||||
switch (type) {
|
||||
case Bool:
|
||||
data.b = false;
|
||||
break;
|
||||
case Int:
|
||||
data.i = 0;
|
||||
break;
|
||||
case Int64:
|
||||
data.l = 0;
|
||||
break;
|
||||
case Double:
|
||||
data.d = 0;
|
||||
break;
|
||||
case String:
|
||||
data.s = new std::string();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Value::~Value() {
|
||||
if (_type == String)
|
||||
delete data.s;
|
||||
}
|
||||
|
||||
Value::Value(const Value &other) {
|
||||
_type = other._type;
|
||||
switch (_type) {
|
||||
case Bool:
|
||||
data.b = other.data.b;
|
||||
break;
|
||||
case Int:
|
||||
data.i = other.data.i;
|
||||
break;
|
||||
case Int64:
|
||||
data.l = other.data.l;
|
||||
break;
|
||||
case Double:
|
||||
data.d = other.data.d;
|
||||
break;
|
||||
case String:
|
||||
data.s = new std::string(*other.data.s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Value::Value(Value &&other) noexcept {
|
||||
_type = other._type;
|
||||
std::swap(data, other.data);
|
||||
other._type = Null;
|
||||
}
|
||||
|
||||
Value &Value::operator=(const Value &other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (_type == String) {
|
||||
delete data.s;
|
||||
}
|
||||
_type = other._type;
|
||||
switch (_type) {
|
||||
case Bool:
|
||||
data.b = other.data.b;
|
||||
break;
|
||||
case Int:
|
||||
data.i = other.data.i;
|
||||
break;
|
||||
case Int64:
|
||||
data.l = other.data.l;
|
||||
break;
|
||||
case Double:
|
||||
data.d = other.data.d;
|
||||
break;
|
||||
case String:
|
||||
data.s = new std::string(*other.data.s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value &Value::operator=(Value &&other) noexcept {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
std::swap(data, other.data);
|
||||
std::swap(_type, other._type);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Value::isEmpty() const {
|
||||
switch (_type) {
|
||||
case Bool:
|
||||
return !data.b;
|
||||
case Int:
|
||||
return data.i == 0;
|
||||
case Int64:
|
||||
return data.l == 0;
|
||||
case Double:
|
||||
return data.d == 0;
|
||||
case String:
|
||||
return data.s->empty();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Value::toBool() const {
|
||||
return _type == Bool && data.b;
|
||||
}
|
||||
|
||||
int Value::toInt() const {
|
||||
switch (_type) {
|
||||
case Bool:
|
||||
return data.b;
|
||||
case Int:
|
||||
return data.i;
|
||||
case Int64:
|
||||
return int(data.l);
|
||||
case Double:
|
||||
return int(data.d);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t Value::toInt64() const {
|
||||
switch (_type) {
|
||||
case Bool:
|
||||
return data.b;
|
||||
case Int:
|
||||
return data.i;
|
||||
case Int64:
|
||||
return data.l;
|
||||
case Double:
|
||||
return int64_t(data.d);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
double Value::toDouble() const {
|
||||
switch (_type) {
|
||||
case Bool:
|
||||
return data.b;
|
||||
case Int:
|
||||
return data.i;
|
||||
case Int64:
|
||||
return double(data.l);
|
||||
case Double:
|
||||
return data.d;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string Value::toString() const {
|
||||
switch (_type) {
|
||||
case Bool:
|
||||
return data.b ? True_Literal : False_Literal;
|
||||
case Int:
|
||||
return std::to_string(data.i);
|
||||
case Int64:
|
||||
return std::to_string(data.l);
|
||||
case Double:
|
||||
return std::to_string(data.d);
|
||||
case String:
|
||||
return *data.s;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool Value::operator==(const Value &other) const {
|
||||
if (_type != other._type)
|
||||
return false;
|
||||
switch (_type) {
|
||||
case Bool:
|
||||
return data.b == other.data.b;
|
||||
case Int:
|
||||
return data.i == other.data.i;
|
||||
case Int64:
|
||||
return data.l == other.data.l;
|
||||
case Double:
|
||||
return data.d == other.data.d;
|
||||
case String:
|
||||
return *data.s == *other.data.s;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Value::operator!=(const Value &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
static void determineBase(std::string &s, int &base, int &f) {
|
||||
if (s.front() == '+') {
|
||||
s = s.substr(1);
|
||||
} else if (s.front() == '-') {
|
||||
f = -1;
|
||||
s = s.substr(1);
|
||||
}
|
||||
if (s.size() > 2 && s.front() == '0') {
|
||||
switch (s.at(1)) {
|
||||
case 'x':
|
||||
case 'X':
|
||||
s = s.substr(2);
|
||||
base = 16;
|
||||
break;
|
||||
case 'b':
|
||||
case 'B':
|
||||
s = s.substr(2);
|
||||
base = 2;
|
||||
break;
|
||||
case 'o':
|
||||
case 'O':
|
||||
s = s.substr(2);
|
||||
base = 8;
|
||||
break;
|
||||
case 'd':
|
||||
case 'D':
|
||||
s = s.substr(2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value Value::fromString(const std::string &s, Value::Type type) {
|
||||
Value res;
|
||||
switch (type) {
|
||||
case Bool: {
|
||||
if (Utils::toLower(s) == True_Literal) {
|
||||
res = true;
|
||||
} else if (Utils::toLower(s) == False_Literal) {
|
||||
res = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Int: {
|
||||
std::string::size_type idx;
|
||||
try {
|
||||
std::string s1 = s;
|
||||
int base = 10;
|
||||
int f = 1;
|
||||
determineBase(s1, base, f);
|
||||
res = std::stoi(s1, &idx, base) * f;
|
||||
if (idx < s1.size()) {
|
||||
res = {};
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Int64: {
|
||||
std::string::size_type idx;
|
||||
try {
|
||||
std::string s1 = s;
|
||||
int base = 10;
|
||||
int f = 1;
|
||||
determineBase(s1, base, f);
|
||||
res = (int64_t) std::stoll(s1, &idx, base) * f;
|
||||
if (idx < s1.size()) {
|
||||
res = {};
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Double: {
|
||||
std::string::size_type idx;
|
||||
try {
|
||||
res = std::stod(s, &idx);
|
||||
if (idx < s.size()) {
|
||||
res = {};
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case String: {
|
||||
if (!s.empty()) {
|
||||
res = s;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::string> Value::toStringList(const std::vector<Value> &values) {
|
||||
std::vector<std::string> res(values.size());
|
||||
std::transform(values.begin(), values.end(), res.begin(), [](const Value &val) {
|
||||
return val.toString(); //
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
const char *Value::typeName(Value::Type type) {
|
||||
const char *expected;
|
||||
switch (type) {
|
||||
case Value::Bool: {
|
||||
expected = "boolean";
|
||||
break;
|
||||
}
|
||||
case Value::Int: {
|
||||
expected = "int";
|
||||
break;
|
||||
}
|
||||
case Value::Int64: {
|
||||
expected = "long";
|
||||
break;
|
||||
}
|
||||
case Value::Double: {
|
||||
expected = "double";
|
||||
break;
|
||||
}
|
||||
case Value::String: {
|
||||
expected = "string";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
expected = "null";
|
||||
break;
|
||||
}
|
||||
return expected;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/syscmdlineTargets.cmake")
|
||||
@@ -0,0 +1 @@
|
||||
add_subdirectory(basic)
|
||||
@@ -0,0 +1,7 @@
|
||||
project(tst_basic)
|
||||
|
||||
file(GLOB _src *.h *.cpp)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${_src})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE syscmdline)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user