chore: baseline usable version

This commit is contained in:
dela
2026-06-10 11:17:51 +08:00
commit a2f82a86b6
932 changed files with 172551 additions and 0 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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);

View File

@@ -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));
}
}

File diff suppressed because it is too large Load Diff

View 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;
}

View 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">&#160;$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 -->

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View File

@@ -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()

View File

@@ -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()

View 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})

View 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()

View 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

View 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
:: ----------------------------------------------------------------------------------

View 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"
)

View File

@@ -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

View File

@@ -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>

View 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
}
}

View 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