CQtDeployer/Deploy/deploycore.cpp

496 lines
18 KiB
C++
Raw Normal View History

2019-01-26 07:54:56 +03:00
/*
2019-12-08 13:57:20 +03:00
* Copyright (C) 2018-2020 QuasarApp.
2019-01-26 07:54:56 +03:00
* Distributed under the lgplv3 software license, see the accompanying
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
2019-09-08 13:37:33 +03:00
#include "extracter.h"
2019-09-03 18:15:05 +03:00
#include "deploycore.h"
2018-12-15 20:51:25 +03:00
#include "quasarapp.h"
2019-11-01 19:43:54 +03:00
#include "pathutils.h"
2018-12-09 17:35:07 +03:00
2018-12-15 20:51:25 +03:00
#include <QDebug>
#include <QDir>
2018-12-09 17:35:07 +03:00
#include <QFileInfo>
2019-08-12 18:05:28 +03:00
#include <QLibraryInfo>
2019-09-14 13:59:11 +03:00
#include <configparser.h>
2018-12-09 17:35:07 +03:00
2019-09-07 12:01:20 +03:00
//QString DeployCore::qtDir = "";
//QStringList DeployCore::extraPaths = QStringList();
2018-12-09 17:35:07 +03:00
2019-03-25 20:13:30 +03:00
2019-09-08 13:37:33 +03:00
const DeployConfig* DeployCore::_config = nullptr;
2019-09-03 18:15:05 +03:00
QtModuleEntry DeployCore::qtModuleEntries[] = {
2019-01-27 15:37:19 +03:00
{ QtBluetoothModule, "bluetooth", "Qt5Bluetooth", nullptr },
{ QtConcurrentModule, "concurrent", "Qt5Concurrent", "qtbase" },
{ QtCoreModule, "core", "Qt5Core", "qtbase" },
{ QtDeclarativeModule, "declarative", "Qt5Declarative", "qtquick1" },
{ QtDesignerModule, "designer", "Qt5Designer", nullptr },
{ QtDesignerComponents, "designercomponents", "Qt5DesignerComponents", nullptr },
{ QtEnginioModule, "enginio", "Enginio", nullptr },
{ QtGamePadModule, "gamepad", "Qt5Gamepad", nullptr },
{ QtGuiModule, "gui", "Qt5Gui", "qtbase" },
{ QtHelpModule, "qthelp", "Qt5Help", "qt_help" },
{ QtMultimediaModule, "multimedia", "Qt5Multimedia", "qtmultimedia" },
{ QtMultimediaWidgetsModule, "multimediawidgets", "Qt5MultimediaWidgets", "qtmultimedia" },
{ QtMultimediaQuickModule, "multimediaquick", "Qt5MultimediaQuick_p", "qtmultimedia" },
{ QtNetworkModule, "network", "Qt5Network", "qtbase" },
{ QtNfcModule, "nfc", "Qt5Nfc", nullptr },
{ QtOpenGLModule, "opengl", "Qt5OpenGL", nullptr },
{ QtPositioningModule, "positioning", "Qt5Positioning", nullptr },
{ QtPrintSupportModule, "printsupport", "Qt5PrintSupport", nullptr },
{ QtQmlModule, "qml", "Qt5Qml", "qtdeclarative" },
{ QtQmlToolingModule, "qmltooling", "qmltooling", nullptr },
{ QtQuickModule, "quick", "Qt5Quick", "qtdeclarative" },
{ QtQuickParticlesModule, "quickparticles", "Qt5QuickParticles", nullptr },
{ QtQuickWidgetsModule, "quickwidgets", "Qt5QuickWidgets", nullptr },
{ QtScriptModule, "script", "Qt5Script", "qtscript" },
{ QtScriptToolsModule, "scripttools", "Qt5ScriptTools", "qtscript" },
{ QtSensorsModule, "sensors", "Qt5Sensors", nullptr },
{ QtSerialPortModule, "serialport", "Qt5SerialPort", "qtserialport" },
{ QtSqlModule, "sql", "Qt5Sql", "qtbase" },
{ QtSvgModule, "svg", "Qt5Svg", nullptr },
{ QtTestModule, "test", "Qt5Test", "qtbase" },
{ QtWebKitModule, "webkit", "Qt5WebKit", nullptr },
{ QtWebKitWidgetsModule, "webkitwidgets", "Qt5WebKitWidgets", nullptr },
{ QtWebSocketsModule, "websockets", "Qt5WebSockets", nullptr },
{ QtWidgetsModule, "widgets", "Qt5Widgets", "qtbase" },
{ QtWinExtrasModule, "winextras", "Qt5WinExtras", nullptr },
{ QtXmlModule, "xml", "Qt5Xml", "qtbase" },
{ QtXmlPatternsModule, "xmlpatterns", "Qt5XmlPatterns", "qtxmlpatterns" },
{ QtWebEngineCoreModule, "webenginecore", "Qt5WebEngineCore", nullptr },
{ QtWebEngineModule, "webengine", "Qt5WebEngine", "qtwebengine" },
{ QtWebEngineWidgetsModule, "webenginewidgets", "Qt5WebEngineWidgets", nullptr },
{ Qt3DCoreModule, "3dcore", "Qt53DCore", nullptr },
{ Qt3DRendererModule, "3drenderer", "Qt53DRender", nullptr },
{ Qt3DQuickModule, "3dquick", "Qt53DQuick", nullptr },
{ Qt3DQuickRendererModule, "3dquickrenderer", "Qt53DQuickRender", nullptr },
{ Qt3DInputModule, "3dinput", "Qt53DInput", nullptr },
{ Qt3DAnimationModule, "3danimation", "Qt53DAnimation", nullptr },
{ Qt3DExtrasModule, "3dextras", "Qt53DExtras", nullptr },
{ QtLocationModule, "geoservices", "Qt5Location", nullptr },
{ QtWebChannelModule, "webchannel", "Qt5WebChannel", nullptr },
{ QtTextToSpeechModule, "texttospeech", "Qt5TextToSpeech", nullptr },
{ QtSerialBusModule, "serialbus", "Qt5SerialBus", nullptr },
{ QtWebViewModule, "webview", "Qt5WebView", nullptr }
};
2019-09-03 18:15:05 +03:00
DeployCore::QtModule DeployCore::getQtModule(const QString& path) {
auto priority = DeployCore::getLibPriority(path);
2019-08-31 22:22:26 +03:00
if (priority != QtLib) {
2019-09-03 18:15:05 +03:00
return DeployCore::QtModule::NONE;
2019-08-31 22:22:26 +03:00
}
int modulesCount = sizeof (qtModuleEntries) / sizeof (QtModuleEntry);
auto lIbName = QFileInfo(path).fileName();
for (int i = 0; i < modulesCount; ++i) {
2019-09-04 16:06:28 +03:00
if (lIbName.contains(qtModuleEntries[i].libraryName)) {
2019-09-03 18:15:05 +03:00
return static_cast<DeployCore::QtModule>(qtModuleEntries[i].module);
2019-08-31 22:22:26 +03:00
}
}
2019-09-03 18:15:05 +03:00
return DeployCore::QtModule::NONE;
2019-08-31 22:22:26 +03:00
}
2019-09-03 18:15:05 +03:00
void DeployCore::addQtModule(DeployCore::QtModule &module, const QString &path) {
2019-09-04 16:06:28 +03:00
2019-09-04 16:37:31 +03:00
QuasarAppUtils::Params::verboseLog("current module " + QString::number(module),
QuasarAppUtils::Info);
2019-09-04 16:06:28 +03:00
auto mod = getQtModule(path);
QuasarAppUtils::Params::verboseLog("add new module from path " + path +
" module value " + QString::number(mod),
QuasarAppUtils::Info);
2019-09-03 18:15:05 +03:00
module = static_cast<DeployCore::QtModule>(
2019-09-04 16:06:28 +03:00
static_cast<quint64>(module) | static_cast<quint64>(mod));
2019-08-31 22:22:26 +03:00
}
2019-09-03 18:15:05 +03:00
LibPriority DeployCore::getLibPriority(const QString &lib) {
2018-12-09 17:35:07 +03:00
if (!QFileInfo(lib).isFile()) {
2019-03-25 21:27:26 +03:00
return NotFile;
2018-12-09 17:35:07 +03:00
}
if (isQtLib(lib)) {
2019-03-25 21:27:26 +03:00
return QtLib;
2018-12-09 17:35:07 +03:00
}
if (isExtraLib(lib)) {
2019-03-25 21:27:26 +03:00
return ExtraLib;
2018-12-09 17:35:07 +03:00
}
2019-04-04 18:11:38 +03:00
return SystemLib;
2018-12-09 17:35:07 +03:00
}
2019-09-03 18:15:05 +03:00
void DeployCore::verboseLog(const QString &str) {
2018-12-15 20:51:25 +03:00
if (QuasarAppUtils::Params::isEndable("verbose")) {
qDebug() << str;
}
}
2019-08-24 17:56:42 +03:00
#define C(X) QuasarAppUtils::Params::isEndable(X)
2019-09-03 18:15:05 +03:00
RunMode DeployCore::getMode() {
2019-08-24 17:56:42 +03:00
if (C("help") || C("h") || C("v") || C("version")) {
return RunMode::Info;
}
2020-01-24 13:57:55 +03:00
if (C("init")) {
return RunMode::Init;
}
2019-09-16 12:38:48 +03:00
if (QuasarAppUtils::Params::customParamasSize() == 0 || C("bin") || C("binDir")) {
2019-08-24 17:56:42 +03:00
return RunMode::Deploy;
}
if (C("clear") || C("force-clear")) {
return RunMode::Clear;
}
return RunMode::Info;
}
2019-09-15 12:02:56 +03:00
QString DeployCore::help() {
2019-08-23 21:29:02 +03:00
QStringList help = {
{ "CQtDeployer version: " + getAppVersion()},
{ "Usage: cqtdeployer <-bin [params]> [options]"},
{ "" },
{ "Options:" },
{ " help / h : Shows help." },
2020-01-27 19:35:02 +03:00
{ " init [params] : will initialize cqtdeployer.json file (configuration file)" },
{ " | For example: cqtdeployer init - for initialize single package configuration" },
{ " | cqtdeployer -init multiPackage - for initialize multi package configuration" },
2019-12-13 23:05:07 +03:00
{ " noOverwrite : Prevents replacing existing files." },
2019-08-23 21:29:02 +03:00
{ " -bin [list, params] : Deployable file or folder." },
{ " | For example -bin /my/project/bin/,/my/project/bin.exe" },
{ " -confFile [params] | The path to the json file with all deployment configurations."},
{ " : Using this file, you can add the necessary options,"},
{ " : thereby simplifying the command invocation in the console."},
{ " : However, the parameters in Kansol have a higher priority than in the file."},
2019-08-23 21:29:02 +03:00
{ " -binDir [params] : A folder which includes deployable files (recursive search)." },
{ " | WARNING: this flag supports 'so', 'dll' and 'exe' files only." },
{ " | Use '-bin' flag if you want to deploy linux binary files" },
2020-01-27 20:02:25 +03:00
{ " -qmlDir [package;path,path]: Qml data dir. For example -qmlDir ~/my/project/qml" },
2019-08-23 21:29:02 +03:00
{ " deploySystem : Deploys all libs" },
2020-01-14 15:48:35 +03:00
{ " deploySystem-with-libc : Skip Deploys system core libs libs" },
2019-08-23 21:29:02 +03:00
{ " -qmake [params] : Qmake path." },
2019-09-24 15:11:30 +03:00
{ " | For example -qmake ~/Qt/5.14.0/gcc_64/bin/qmake" },
2019-08-23 21:29:02 +03:00
{ " -ignore [list,params] : The list of libs to ignore." },
{ " | For example -ignore libicudata.so.56,libicudata2.so.56" },
{ " -ignoreEnv [list,params] : The list of the environment to ignore" },
{ " | For example -ignoreEnv /bad/dir,/my/bad/Dir" },
{ " clear : Deletes deployable files of the previous session." },
2019-08-24 01:42:13 +03:00
{ " force-clear : Deletes the destination directory before deployment." },
2019-08-23 21:29:02 +03:00
{ " allQmlDependes : Extracts all the qml libraries." },
{ " | (not recommended, as it takes great amount of computer memory)" },
{ " -libDir [list,params] : Sets additional paths for extra libs of an app." },
{ " | For example -libDir /myLib,/newLibs " },
2019-11-05 16:23:37 +03:00
{ " -extraLibs [list,params] | Sets the mask of the library name for forced copying."},
{ " : For example: '-extraLib mySql' - forces to copy all libraries"},
{ " : whose names contain mySql to the project folder. This option is case sensitive."},
{ " : This option is case sensitive."},
2019-08-23 21:29:02 +03:00
{ " -extraPlugin[list,params]: Sets an additional path to extraPlugin of an app" },
{ " -recursiveDepth [params] : Sets the Depth of recursive search of libs (default 0)" },
{ " -targetDir [params] : Sets target directory(by default it is the path to the first deployable file)" },
2020-01-27 20:02:25 +03:00
{ " -targetPackage [tar1;path,path]: Sets package for target( by default it is empty value)" },
2019-08-23 21:29:02 +03:00
{ " noStrip : Skips strip step" },
2019-11-06 12:38:15 +03:00
{ " extractPlugins | This flag will cause cqtdeployer to retrieve dependencies from plugins." },
{ " : Starting with version 1.4, this option has been disabled by default,"},
{ " : as it can add low-level graphics libraries to the distribution," },
{ " : which will not be compatible with equipment on users' hosts."},
2019-08-23 21:29:02 +03:00
{ " noTranslations : Skips the translations files." },
{ " | It doesn't work without qmake and inside a snap package" },
2019-11-14 17:35:21 +03:00
{ " -noAutoCheckQmake : Disables automatic search of paths to qmake in executable files." },
2020-01-27 20:02:25 +03:00
{ " -qmlOut [package;val,val] : Sets path to qml out directory" },
{ " -libOut [package;val,val] : Sets path to libraries out directory" },
{ " -trOut [package;val,val] : Sets path to translations out directory" },
{ " -pluginOut [package;val,val]: Sets path to plugins out directory" },
{ " -binOut [package;val,val] : Sets path to binary out directory" },
{ " -recOut [package;val,val] : Sets path to recurses out directory" },
{ " -name [package;val,val] : Sets name for package. If this if you do not specify a package, the value will be assigned to the default package ("")" },
{ " -description [package;val,val] : Sets description for package" },
{ " -deployVersion [package;val,val] : Sets version for package" },
{ " -releaseDate [package;val,val] : Sets release date for package" },
{ " -icon [package;val,val] : Sets path to icon for package" },
{ " -publisher [package;val,val]: Sets Publisher for package" },
2019-11-25 13:04:47 +03:00
{ " -qif [params] : Create the QIF installer for deployement programm" },
{ " : By default params value is 'Default'" },
{ " : Examples:" },
{ " : cqtdeployer qif - for use default templates of qt installer framework." },
{ " : cqtdeployer -qif path/to/folder/with/qifTemplate - for use custom templates of qt installer framework." },
2019-12-27 12:51:03 +03:00
{ " -customScript [scriptCode]: Insert extra code inTo All run script."},
2019-08-23 23:10:31 +03:00
{ " v / version : Shows compiled version" },
{ " verbose [1-3] : Shows debug log" },
2019-08-23 21:29:02 +03:00
2019-08-23 23:10:31 +03:00
{ "" },
2019-08-23 21:29:02 +03:00
{ "" },
{ "Example: cqtdeployer -bin myApp -qmlDir ~/Qt/5.14.0/gcc_64/qml -qmake ~/Qt/5.14.0/gcc_64/bin/qmake clear" },
2019-08-23 23:10:31 +03:00
{ "Example (only C libs): cqtdeployer -bin myApp clear" }};
2019-08-23 21:29:02 +03:00
QuasarAppUtils::Params::showHelp(help);
2019-09-10 18:22:49 +03:00
return help.join(" ");
2018-12-15 20:51:25 +03:00
}
2019-09-10 15:40:12 +03:00
QStringList DeployCore::helpKeys() {
return {
"help",
2019-09-12 09:38:30 +03:00
"noOverwrite",
2019-09-10 15:40:12 +03:00
"bin",
"binDir",
"qmlDir",
"deploySystem",
2019-09-24 15:11:30 +03:00
"deploySystem-with-libc",
2019-09-10 15:40:12 +03:00
"qmake",
"ignore",
"ignoreEnv",
"clear",
"force-clear",
"allQmlDependes",
"libDir",
2019-11-05 16:52:22 +03:00
"extraLibs",
2019-09-10 15:40:12 +03:00
"extraPlugin",
"recursiveDepth",
"targetDir",
2020-01-27 20:02:25 +03:00
"targetPackage",
2019-09-10 15:40:12 +03:00
"noStrip",
2019-11-05 18:24:55 +03:00
"extractPlugins",
2019-09-10 15:40:12 +03:00
"noTranslations",
"qmlOut",
"libOut",
"trOut",
"pluginOut",
"binOut",
"recOut",
2019-09-10 15:40:12 +03:00
"version",
2019-11-14 17:35:21 +03:00
"verbose",
2019-11-25 18:47:58 +03:00
"qif",
2020-01-14 15:48:35 +03:00
"noAutoCheckQmake",
"name",
"description",
"deployVersion",
"releaseDate",
2020-01-15 11:51:11 +03:00
"icon",
2020-01-18 16:38:40 +03:00
"publisher",
2019-12-27 12:51:03 +03:00
"customScript"
2019-09-10 15:40:12 +03:00
};
2019-08-24 17:56:42 +03:00
}
2019-12-15 18:30:24 +03:00
QStringList DeployCore::extractTranslation(const QSet<QString> &libs) {
2019-01-27 15:37:19 +03:00
QSet<QString> res;
const size_t qtModulesCount = sizeof(qtModuleEntries) / sizeof(QtModuleEntry);
2020-01-12 16:43:03 +03:00
for (const auto &lib: libs) {
2019-01-27 15:37:19 +03:00
for (size_t i = 0; i < qtModulesCount; ++i) {
if (lib.contains(qtModuleEntries[i].libraryName) &&
qtModuleEntries[i].translation) {
res.insert(qtModuleEntries[i].translation);
}
}
}
return res.toList();
}
2019-09-03 18:15:05 +03:00
QString DeployCore::getAppVersion() {
2019-08-12 18:05:28 +03:00
return APP_VERSION;
}
2019-09-03 18:15:05 +03:00
QString DeployCore::getQtVersion() {
2019-08-12 18:05:28 +03:00
#ifdef QT_VERSION_STR
return QT_VERSION_STR;
#else
return "without qt";
#endif
}
2019-09-03 18:15:05 +03:00
void DeployCore::printVersion() {
2019-08-12 18:05:28 +03:00
qInfo() << "CQtDeployer: " + getAppVersion();
qInfo() << "Qt: " + getQtVersion();
}
2019-09-15 11:31:31 +03:00
bool DeployCore::isExecutable(const QFileInfo& file) {
2019-08-28 17:23:49 +03:00
auto sufix = file.completeSuffix();
return sufix.contains("exe", Qt::CaseInsensitive) || sufix.contains("run", Qt::CaseInsensitive) || sufix.isEmpty();
}
2020-01-02 19:27:40 +03:00
bool DeployCore::isContainsArraySeparators(const QString &val, int lastLvl) {
while (lastLvl) {
if (val.contains(getSeparator(--lastLvl)))
return true;
}
return false;
}
2020-01-28 13:51:00 +03:00
QString DeployCore::findProcess(const QString &env, const QString& proc) {
auto list = env.split(DeployCore::getEnvSeparator());
for (const auto& path : list) {
auto files = QDir(path).entryInfoList(QDir::NoDotAndDotDot | QDir::Files);
for (const auto& bin : files) {
if (bin.fileName().compare(proc, ONLY_WIN_CASE_INSENSIATIVE) == 0) {
return bin.absoluteFilePath();
}
}
}
return "";
}
2019-09-08 13:37:33 +03:00
int DeployCore::find(const QString &str, const QStringList &list) {
for (int i = 0 ; i < list.size(); ++i) {
if (list[i].contains(str))
return i;
}
return -1;
}
bool DeployCore::isLib(const QFileInfo &file) {
return file.completeSuffix().contains("so", Qt::CaseInsensitive)
|| file.completeSuffix().contains("dll", Qt::CaseInsensitive);
}
2019-11-01 15:12:03 +03:00
MSVCVersion DeployCore::getMSVC(const QString &_qtBin) {
2019-03-28 10:32:46 +03:00
int res = MSVCVersion::MSVC_Unknown;
2019-11-01 15:12:03 +03:00
QDir dir = QFileInfo(_qtBin).absoluteFilePath();
2019-03-28 10:32:46 +03:00
if (!dir.cdUp()) {
QuasarAppUtils::Params::verboseLog("is not standart qt repo");
return static_cast<MSVCVersion>(res);
}
auto msvcPath = dir.absolutePath();
if (!(dir.cdUp() && dir.cdUp())) {
QuasarAppUtils::Params::verboseLog("is not standart qt repo");
return static_cast<MSVCVersion>(res);
}
if (!msvcPath.contains("msvc")) {
QuasarAppUtils::Params::verboseLog("vcredis not defined");
return static_cast<MSVCVersion>(res);
}
auto base = msvcPath.mid(msvcPath.indexOf("msvc"), 11);
auto version = base.mid(4 , 4);
auto type = base.right(2);
if (version == "2013") {
res |= MSVC_13;
}
else if (version == "2015") {
res |= MSVC_15;
}
else if (version == "2017") {
res |= MSVC_17;
}
else if (version == "2019") {
res |= MSVC_19;
}
if (type == "32") {
res |= MSVC_x32;
}
else if (type == "64") {
res |= MSVC_x64;
}
return static_cast<MSVCVersion>(res);
}
2019-11-01 15:12:03 +03:00
QString DeployCore::getVCredist(const QString &_qtbinDir) {
auto msvc = getMSVC(_qtbinDir);
2019-03-28 12:36:21 +03:00
2019-11-01 15:12:03 +03:00
QDir dir = _qtbinDir;
2019-03-28 12:36:21 +03:00
if (!(dir.cdUp() && dir.cdUp() && dir.cdUp() && dir.cd("vcredist"))) {
QuasarAppUtils::Params::verboseLog("redist not findet!");
return "";
}
auto infoList = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
auto name = getMSVCName(msvc);
auto version = getMSVCVersion(msvc);
2020-01-12 16:43:03 +03:00
for (const auto &info: infoList) {
2019-11-01 15:12:03 +03:00
auto file = info.fileName();
if (file.contains(name, Qt::CaseInsensitive) &&
file.contains(version, Qt::CaseInsensitive)) {
2019-03-28 12:36:21 +03:00
return info.absoluteFilePath();
}
}
return "";
}
2019-09-03 18:15:05 +03:00
QString DeployCore::getMSVCName(MSVCVersion msvc) {
2019-03-28 12:36:21 +03:00
if (msvc | MSVCVersion::MSVC_13) {
return "msvc2013";
} else if (msvc | MSVCVersion::MSVC_15) {
return "msvc2015";
} else if (msvc | MSVCVersion::MSVC_17) {
return "msvc2017";
} else if (msvc | MSVCVersion::MSVC_19) {
return "msvc2019";
}
return "";
}
2019-09-03 18:15:05 +03:00
QString DeployCore::getMSVCVersion(MSVCVersion msvc) {
2019-03-28 12:36:21 +03:00
if (msvc | MSVCVersion::MSVC_x32) {
return "x86";
} else if (msvc | MSVCVersion::MSVC_x64) {
return "x64";
}
2019-03-28 10:32:46 +03:00
return "";
}
2019-09-03 18:15:05 +03:00
bool DeployCore::isQtLib(const QString &lib) {
2019-11-01 19:43:54 +03:00
QFileInfo info((lib));
return _config->qtDir.isQt(PathUtils::toFullPath(info.absoluteFilePath()));
2018-12-09 17:35:07 +03:00
}
2019-09-03 18:15:05 +03:00
bool DeployCore::isExtraLib(const QString &lib) {
2018-12-09 17:35:07 +03:00
QFileInfo info(lib);
2019-11-05 13:17:52 +03:00
return _config->extraPaths.contains(info.absoluteFilePath());
2018-12-09 17:35:07 +03:00
}
QChar DeployCore::getSeparator(int lvl) {
switch (lvl) {
case 0: return ',';
case 1: return ';';
default:
return '\0';
}
}
2020-01-16 11:10:16 +03:00
char DeployCore::getEnvSeparator() {
#ifdef Q_OS_UNIX
return ':';
#else
return ';';
#endif
}
2020-01-18 16:38:40 +03:00
2019-12-24 11:46:11 +03:00
uint qHash(WinAPI i) {
return static_cast<uint>(i);
}