Autogen: AUTO*_EXECUTABLE: add support for per-config values

* Per-config values were added to `AUTO*_EXECUTABLE`.
* Dependency order was refactored for `cmake_autogen` and `cmake_autorcc` to avoid unnecessary rebuilds.
* A new parameter was added for `cmake_autogen` and `cmake_autorcc` to specify the config name of the `auto*_executable` to be used.
* Add `AUTOGEN_BETTER_GRAPH_MULTI_CONFIG` target property to change the behavior of the dependency graph.
* The timestamp target is split into three targets for per-config to avoid redundant `mocs_compilation` builds when `AUTOGEN_BETTER_GRAPH_MULTI_CONFIG`	 is ON
* Per-config `DEP_FILE_RULE_NAME` values were added to `AutogenInfo.json` for `Multi-Config` usage.
* Some functions were refactored to avoid code duplication.

This commit reimplements fddd0f0443

Fixes: #20074
stage/master/nightly/2024/01/18
Orkun Tokdemir 2023-10-18 15:00:57 +02:00
parent 31dead97ed
commit 7c39dabdbc
30 changed files with 1003 additions and 212 deletions

View File

@ -78,6 +78,7 @@ syn keyword cmakeProperty contained
\ AUTOGEN_TARGETS_FOLDER
\ AUTOGEN_TARGET_DEPENDS
\ AUTOGEN_USE_SYSTEM_INCLUDE
\ AUTOGEN_BETTER_GRAPH_MULTI_CONFIG
\ AUTOMOC
\ AUTOMOC_COMPILER_PREDEFINES
\ AUTOMOC_DEPEND_FILTERS
@ -766,6 +767,7 @@ syn keyword cmakeVariable contained
\ CMAKE_ASM_STANDARD_REQUIRED
\ CMAKE_ASM_SUPPORTED
\ CMAKE_ASM_VISIBILITY_PRESET
\ CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG
\ CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX
\ CMAKE_AUTOGEN_ORIGIN_DEPENDS
\ CMAKE_AUTOGEN_PARALLEL

View File

@ -129,6 +129,7 @@ Properties on Targets
/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY_CONFIG
/prop_tgt/ARCHIVE_OUTPUT_NAME
/prop_tgt/ARCHIVE_OUTPUT_NAME_CONFIG
/prop_tgt/AUTOGEN_BETTER_GRAPH_MULTI_CONFIG
/prop_tgt/AUTOGEN_BUILD_DIR
/prop_tgt/AUTOGEN_COMMAND_LINE_LENGTH_MAX
/prop_tgt/AUTOGEN_ORIGIN_DEPENDS

View File

@ -389,6 +389,7 @@ Variables that Control the Build
/variable/CMAKE_APPLE_SILICON_PROCESSOR
/variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY
/variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY_CONFIG
/variable/CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG
/variable/CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX
/variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS
/variable/CMAKE_AUTOGEN_PARALLEL

View File

@ -0,0 +1,22 @@
AUTOGEN_BETTER_GRAPH_MULTI_CONFIG
---------------------------------
.. versionadded:: 3.29
``AUTOGEN_BETTER_GRAPH_MULTI_CONFIG`` is a boolean property that can be set
on a target to have better dependency graph for multi-configuration generators.
When this property is enabled, ``CMake`` will generate more per-config targets.
Thus, the dependency graph will be more accurate for multi-configuration
generators and some recompilations will be avoided.
If the Qt version is 6.8 or newer, this property is enabled by default.
If the Qt version is older than 6.8, this property is disabled by default.
Consult the Qt documentation to check if the property can be enabled for older
Qt versions.
See the :manual:`cmake-qt(7)` manual for more information on using CMake
with Qt.
This property is initialized by the
:variable:`CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG` variable if it is set when
a target is created.

View File

@ -0,0 +1,10 @@
CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG
---------------------------------------
.. versionadded:: 3.29
This variable is used to initialize the
:prop_tgt:`AUTOGEN_BETTER_GRAPH_MULTI_CONFIG` property on all targets as they
are created. See that target property for additional information.
By default ``CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG`` is unset.

View File

@ -6,6 +6,7 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <cm/string_view>
@ -16,6 +17,22 @@
class cmQtAutoGen
{
public:
/** String value with per configuration variants. */
class ConfigString
{
public:
std::string Default;
std::unordered_map<std::string, std::string> Config;
};
/** String values with per configuration variants. */
template <typename C>
class ConfigStrings
{
public:
C Default;
std::unordered_map<std::string, C> Config;
};
/** Integer version. */
struct IntegerVersion
{

View File

@ -213,24 +213,81 @@ void cmQtAutoGenGlobalInitializer::AddToGlobalAutoRcc(
}
}
cmQtAutoGen::CompilerFeaturesHandle
cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>
cmQtAutoGenGlobalInitializer::GetCompilerFeatures(
std::string const& generator, std::string const& executable,
std::string& error)
std::string const& generator, cmQtAutoGen::ConfigString const& executable,
std::string& error, bool const isMultiConfig, bool UseBetterGraph)
{
cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle> res;
if (isMultiConfig && UseBetterGraph) {
for (auto const& config : executable.Config) {
auto const exe = config.second;
// Check if we have cached features
{
auto it = this->CompilerFeatures_.Config[config.first].find(exe);
if (it != this->CompilerFeatures_.Config[config.first].end()) {
res.Config[config.first] = it->second;
continue;
}
}
// Check if the executable exists
if (!cmSystemTools::FileExists(exe, true)) {
error = cmStrCat("The \"", generator, "\" executable ",
cmQtAutoGen::Quoted(exe), " does not exist.");
res.Config[config.first] = {};
continue;
}
// Test the executable
std::string stdOut;
{
std::string stdErr;
std::vector<std::string> command;
command.emplace_back(exe);
command.emplace_back("-h");
int retVal = 0;
const bool runResult = cmSystemTools::RunSingleCommand(
command, &stdOut, &stdErr, &retVal, nullptr,
cmSystemTools::OUTPUT_NONE, cmDuration::zero(),
cmProcessOutput::Auto);
if (!runResult) {
error = cmStrCat("Test run of \"", generator, "\" executable ",
cmQtAutoGen::Quoted(exe), " failed.\n",
cmQtAutoGen::QuotedCommand(command), '\n', stdOut,
'\n', stdErr);
res.Config[config.first] = {};
continue;
}
}
// Create valid handle
res.Config[config.first] =
std::make_shared<cmQtAutoGen::CompilerFeatures>();
res.Config[config.first]->HelpOutput = std::move(stdOut);
// Register compiler features
this->CompilerFeatures_.Config[config.first].emplace(
exe, res.Config[config.first]);
}
return res;
}
// Check if we have cached features
{
auto it = this->CompilerFeatures_.find(executable);
if (it != this->CompilerFeatures_.end()) {
return it->second;
auto it = this->CompilerFeatures_.Default.find(executable.Default);
if (it != this->CompilerFeatures_.Default.end()) {
res.Default = it->second;
return res;
}
}
// Check if the executable exists
if (!cmSystemTools::FileExists(executable, true)) {
error = cmStrCat("The \"", generator, "\" executable ",
cmQtAutoGen::Quoted(executable), " does not exist.");
return cmQtAutoGen::CompilerFeaturesHandle();
if (!cmSystemTools::FileExists(executable.Default, true)) {
error =
cmStrCat("The \"", generator, "\" executable ",
cmQtAutoGen::Quoted(executable.Default), " does not exist.");
return cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>();
}
// Test the executable
@ -238,7 +295,7 @@ cmQtAutoGenGlobalInitializer::GetCompilerFeatures(
{
std::string stdErr;
std::vector<std::string> command;
command.emplace_back(executable);
command.emplace_back(executable.Default);
command.emplace_back("-h");
int retVal = 0;
const bool runResult = cmSystemTools::RunSingleCommand(
@ -246,20 +303,18 @@ cmQtAutoGenGlobalInitializer::GetCompilerFeatures(
cmDuration::zero(), cmProcessOutput::Auto);
if (!runResult) {
error = cmStrCat("Test run of \"", generator, "\" executable ",
cmQtAutoGen::Quoted(executable), " failed.\n",
cmQtAutoGen::Quoted(executable.Default), " failed.\n",
cmQtAutoGen::QuotedCommand(command), '\n', stdOut, '\n',
stdErr);
return cmQtAutoGen::CompilerFeaturesHandle();
return cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>();
}
}
// Create valid handle
cmQtAutoGen::CompilerFeaturesHandle res =
std::make_shared<cmQtAutoGen::CompilerFeatures>();
res->HelpOutput = std::move(stdOut);
res.Default = std::make_shared<cmQtAutoGen::CompilerFeatures>();
res.Default->HelpOutput = std::move(stdOut);
// Register compiler features
this->CompilerFeatures_.emplace(executable, res);
this->CompilerFeatures_.Default.emplace(executable.Default, res.Default);
return res;
}

View File

@ -66,14 +66,17 @@ private:
void AddToGlobalAutoRcc(cmLocalGenerator* localGen,
std::string const& targetName);
cmQtAutoGen::CompilerFeaturesHandle GetCompilerFeatures(
std::string const& generator, std::string const& executable,
std::string& error);
cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>
GetCompilerFeatures(std::string const& generator,
cmQtAutoGen::ConfigString const& executable,
std::string& error, bool isMultiConfig,
bool UseBetterGraph);
std::vector<std::unique_ptr<cmQtAutoGenInitializer>> Initializers_;
std::map<cmLocalGenerator*, std::string> GlobalAutoGenTargets_;
std::map<cmLocalGenerator*, std::string> GlobalAutoRccTargets_;
std::unordered_map<std::string, cmQtAutoGen::CompilerFeaturesHandle>
cmQtAutoGen::ConfigStrings<
std::unordered_map<std::string, cmQtAutoGen::CompilerFeaturesHandle>>
CompilerFeatures_;
Keywords const Keywords_;
};

View File

@ -2,6 +2,7 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmQtAutoGenInitializer.h"
#include <array>
#include <cstddef>
#include <deque>
#include <initializer_list>
@ -17,6 +18,7 @@
#include <cm/algorithm>
#include <cm/iterator>
#include <cm/memory>
#include <cm/string_view>
#include <cmext/algorithm>
#include <cmext/string_view>
@ -301,15 +303,22 @@ bool InfoWriter::Save(std::string const& filename)
return fileStream.Close();
}
void AddAutogenExecutableToDependencies(
cmQtAutoGenInitializer::GenVarsT const& genVars,
std::vector<std::string>& dependencies)
cmQtAutoGen::ConfigStrings<std::vector<std::string>> generateListOptions(
cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle> const&
executableFeatures,
bool IsMultiConfig)
{
if (genVars.ExecutableTarget != nullptr) {
dependencies.push_back(genVars.ExecutableTarget->Target->GetName());
} else if (!genVars.Executable.empty()) {
dependencies.push_back(genVars.Executable);
cmQtAutoGen::ConfigStrings<std::vector<std::string>> tempListOptions;
if (IsMultiConfig) {
for (auto const& executableFeature : executableFeatures.Config) {
tempListOptions.Config[executableFeature.first] =
executableFeature.second->ListOptions;
}
} else {
tempListOptions.Default = executableFeatures.Default->ListOptions;
}
return tempListOptions;
}
} // End of unnamed namespace
@ -334,6 +343,42 @@ cmQtAutoGenInitializer::cmQtAutoGenInitializer(
this->Rcc.GlobalTarget = globalAutoRccTarget;
this->CrossConfig =
!this->Makefile->GetSafeDefinition("CMAKE_CROSS_CONFIGS").empty();
this->UseBetterGraph =
this->GenTarget->GetProperty("AUTOGEN_BETTER_GRAPH_MULTI_CONFIG").IsSet()
? this->GenTarget->GetProperty("AUTOGEN_BETTER_GRAPH_MULTI_CONFIG").IsOn()
: (this->QtVersion >= IntegerVersion(6, 8));
// AUTOGEN_BETTER_GRAPH_MULTI_CONFIG is set explicitly because it is read by
// the qt library
this->GenTarget->Target->SetProperty("AUTOGEN_BETTER_GRAPH_MULTI_CONFIG",
this->UseBetterGraph ? "ON" : "OFF");
}
void cmQtAutoGenInitializer::AddAutogenExecutableToDependencies(
cmQtAutoGenInitializer::GenVarsT const& genVars,
std::vector<std::string>& dependencies) const
{
if (genVars.ExecutableTarget != nullptr) {
dependencies.push_back(genVars.ExecutableTarget->Target->GetName());
} else if (this->MultiConfig && this->UseBetterGraph) {
cm::string_view const& configGenexWithCommandConfig =
"$<COMMAND_CONFIG:$<$<CONFIG:";
cm::string_view const& configGenex = "$<$<CONFIG:";
cm::string_view const& configGenexEnd = ">";
cm::string_view const& configGenexEndWithCommandConfig = ">>";
auto genexBegin =
this->CrossConfig ? configGenexWithCommandConfig : configGenex;
auto genexEnd =
this->CrossConfig ? configGenexEndWithCommandConfig : configGenexEnd;
for (auto const& config : genVars.Executable.Config) {
auto executableWithConfig =
cmStrCat(genexBegin, config.first, ">:", config.second, genexEnd);
dependencies.emplace_back(std::move(executableWithConfig));
}
} else {
if (!genVars.Executable.Default.empty()) {
dependencies.push_back(genVars.Executable.Default);
}
}
}
bool cmQtAutoGenInitializer::InitCustomTargets()
@ -811,18 +856,30 @@ bool cmQtAutoGenInitializer::InitRcc()
return false;
}
// Evaluate test output on demand
CompilerFeatures& features = *this->Rcc.ExecutableFeatures;
if (!features.Evaluated) {
// Look for list options
if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) {
if (features.HelpOutput.find("--list") != std::string::npos) {
features.ListOptions.emplace_back("--list");
} else if (features.HelpOutput.find("-list") != std::string::npos) {
features.ListOptions.emplace_back("-list");
auto& features = this->Rcc.ExecutableFeatures;
auto checkAndAddOptions = [this](CompilerFeaturesHandle& feature) {
if (!feature->Evaluated) {
// Look for list options
if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) {
static std::array<std::string, 2> const listOptions{ { "--list",
"-list" } };
for (std::string const& opt : listOptions) {
if (feature->HelpOutput.find(opt) != std::string::npos) {
feature->ListOptions.emplace_back(opt);
break;
}
}
}
// Evaluation finished
feature->Evaluated = true;
}
// Evaluation finished
features.Evaluated = true;
};
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
checkAndAddOptions(features.Config[config]);
}
} else {
checkAndAddOptions(features.Default);
}
}
@ -1158,8 +1215,14 @@ bool cmQtAutoGenInitializer::InitScanFiles()
// Path checksum
qrc.QrcPathChecksum = this->PathCheckSum.getPart(qrc.QrcFile);
// Output file name
qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
"/qrc_", qrc.QrcName, ".cpp");
if (this->MultiConfig && !this->GlobalGen->IsXcode() &&
this->UseBetterGraph) {
qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
"_$<CONFIG>", "/qrc_", qrc.QrcName, ".cpp");
} else {
qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
"/qrc_", qrc.QrcName, ".cpp");
}
std::string const base = cmStrCat(this->Dir.Info, "/AutoRcc_",
qrc.QrcName, '_', qrc.QrcPathChecksum);
qrc.LockFile = cmStrCat(base, "_Lock.lock");
@ -1191,11 +1254,25 @@ bool cmQtAutoGenInitializer::InitScanFiles()
for (Qrc& qrc : this->Rcc.Qrcs) {
if (!qrc.Generated) {
std::string error;
RccLister const lister(this->Rcc.Executable,
this->Rcc.ExecutableFeatures->ListOptions);
if (!lister.list(qrc.QrcFile, qrc.Resources, error)) {
cmSystemTools::Error(error);
return false;
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
RccLister const lister(
this->Rcc.Executable.Config[config],
this->Rcc.ExecutableFeatures.Config[config]->ListOptions);
if (!lister.list(qrc.QrcFile, qrc.Resources.Config[config],
error)) {
cmSystemTools::Error(error);
return false;
}
}
} else {
RccLister const lister(
this->Rcc.Executable.Default,
this->Rcc.ExecutableFeatures.Default->ListOptions);
if (!lister.list(qrc.QrcFile, qrc.Resources.Default, error)) {
cmSystemTools::Error(error);
return false;
}
}
}
}
@ -1223,8 +1300,9 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
if (this->Moc.Enabled) {
this->AddGeneratedSource(this->Moc.CompilationFile, this->Moc, true);
if (useDepfile) {
if (this->MultiConfig && this->CrossConfig &&
this->GlobalGen->GetName().find("Ninja") != std::string::npos) {
if (this->CrossConfig &&
this->GlobalGen->GetName().find("Ninja") != std::string::npos &&
!this->UseBetterGraph) {
// Make all mocs_compilation_<CONFIG>.cpp files byproducts of the
// ${target}_autogen/timestamp custom command.
// We cannot just use Moc.CompilationFileGenex here, because that
@ -1267,28 +1345,11 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
// Compose command lines
// FIXME: Take advantage of our per-config mocs_compilation_$<CONFIG>.cpp
// instead of fiddling with the include directories
std::vector<std::string> configs;
this->GlobalGen->GetQtAutoGenConfigs(configs);
bool constexpr stdPipesUTF8 = true;
cmCustomCommandLines commandLines;
if (!this->CrossConfig) {
std::string autogenInfoFileConfig;
if (this->MultiConfig) {
autogenInfoFileConfig = "$<CONFIG>";
} else {
autogenInfoFileConfig = configs[0];
}
commandLines.push_back(cmMakeCommandLine(
{ cmSystemTools::GetCMakeCommand(), "-E", "cmake_autogen",
this->AutogenTarget.InfoFile, autogenInfoFileConfig }));
} else {
for (auto const& config : configs) {
commandLines.push_back(cmMakeCommandLine(
{ cmSystemTools::GetCMakeCommand(), "-E", "cmake_autogen",
this->AutogenTarget.InfoFile, config }));
}
}
AddCMakeProcessToCommandLines(this->AutogenTarget.InfoFile, "cmake_autogen",
commandLines);
// Use PRE_BUILD on demand
bool usePRE_BUILD = false;
@ -1456,18 +1517,47 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
AddAutogenExecutableToDependencies(this->Moc, dependencies);
AddAutogenExecutableToDependencies(this->Uic, dependencies);
std::string outputFile;
std::string depFile;
// Create the custom command that outputs the timestamp file.
const char timestampFileName[] = "timestamp";
const std::string outputFile =
cmStrCat(this->Dir.Build, "/", timestampFileName);
this->AutogenTarget.DepFile = cmStrCat(this->Dir.Build, "/deps");
this->AutogenTarget.DepFileRuleName =
cmStrCat(this->Dir.RelativeBuild, "/", timestampFileName);
commandLines.push_back(cmMakeCommandLine(
{ cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
if (this->MultiConfig && this->UseBetterGraph) {
// create timestamp file with $<CONFIG> in the name so that
// every cmake_autogen target has its own timestamp file
std::string const configView = "$<CONFIG>";
std::string const timestampFileWithoutConfig = "timestamp_";
std::string const depFileWithoutConfig =
cmStrCat(this->Dir.Build, "/deps_");
std::string const timestampFileName =
timestampFileWithoutConfig + configView;
outputFile = cmStrCat(this->Dir.Build, "/", timestampFileName);
auto const depFileWithConfig =
cmStrCat(depFileWithoutConfig, configView);
depFile = depFileWithConfig;
commandLines.push_back(cmMakeCommandLine(
{ cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
this->AddGeneratedSource(outputFile, this->Moc);
ConfigString outputFileWithConfig;
for (std::string const& config : this->ConfigsList) {
auto tempTimestampFileName = timestampFileWithoutConfig + config;
auto tempDepFile = depFileWithoutConfig + config;
outputFileWithConfig.Config[config] = tempTimestampFileName;
this->AutogenTarget.DepFileRuleName.Config[config] =
cmStrCat(this->Dir.RelativeBuild, "/", tempTimestampFileName);
this->AutogenTarget.DepFile.Config[config] = tempDepFile;
}
this->AddGeneratedSource(outputFileWithConfig, this->Moc);
} else {
cm::string_view const timestampFileName = "timestamp";
outputFile = cmStrCat(this->Dir.Build, "/", timestampFileName);
this->AutogenTarget.DepFile.Default =
cmStrCat(this->Dir.Build, "/deps");
depFile = this->AutogenTarget.DepFile.Default;
this->AutogenTarget.DepFileRuleName.Default =
cmStrCat(this->Dir.RelativeBuild, "/", timestampFileName);
commandLines.push_back(cmMakeCommandLine(
{ cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
this->AddGeneratedSource(outputFile, this->Moc);
}
cc = cm::make_unique<cmCustomCommand>();
cc->SetOutputs(outputFile);
cc->SetByproducts(timestampByproducts);
@ -1476,14 +1566,11 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
cc->SetComment(autogenComment.c_str());
cc->SetWorkingDirectory(this->Dir.Work.c_str());
cc->SetEscapeOldStyle(false);
cc->SetDepfile(this->AutogenTarget.DepFile);
cc->SetDepfile(depFile);
cc->SetStdPipesUTF8(stdPipesUTF8);
this->LocalGen->AddCustomCommandToOutput(std::move(cc));
// Alter variables for the autogen target which now merely wraps the
// custom command
dependencies.clear();
dependencies.emplace_back(outputFile);
dependencies.emplace_back(std::move(outputFile));
commandLines.clear();
autogenComment.clear();
}
@ -1535,6 +1622,36 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
return true;
}
void cmQtAutoGenInitializer::AddCMakeProcessToCommandLines(
std::string const& infoFile, std::string const& processName,
cmCustomCommandLines& commandLines)
{
if (this->CrossConfig && this->UseBetterGraph) {
commandLines.push_back(cmMakeCommandLine(
{ cmSystemTools::GetCMakeCommand(), "-E", processName, infoFile,
"$<CONFIG>", "$<COMMAND_CONFIG:$<CONFIG>>" }));
} else if ((this->MultiConfig && this->GlobalGen->IsXcode()) ||
this->CrossConfig) {
for (std::string const& config : this->ConfigsList) {
commandLines.push_back(
cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
processName, infoFile, config }));
}
} else {
std::string autoInfoFileConfig;
if (this->MultiConfig) {
autoInfoFileConfig = "$<CONFIG>";
} else {
std::vector<std::string> configs;
this->GlobalGen->GetQtAutoGenConfigs(configs);
autoInfoFileConfig = configs[0];
}
commandLines.push_back(
cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", processName,
infoFile, autoInfoFileConfig }));
}
}
bool cmQtAutoGenInitializer::InitRccTargets()
{
for (Qrc const& qrc : this->Rcc.Qrcs) {
@ -1555,18 +1672,7 @@ bool cmQtAutoGenInitializer::InitRccTargets()
ccDepends.push_back(qrc.InfoFile);
cmCustomCommandLines commandLines;
if (this->MultiConfig) {
// Build for all configurations
for (std::string const& config : this->ConfigsList) {
commandLines.push_back(
cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
"cmake_autorcc", qrc.InfoFile, config }));
}
} else {
commandLines.push_back(
cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
"cmake_autorcc", qrc.InfoFile, "$<CONFIG>" }));
}
AddCMakeProcessToCommandLines(qrc.InfoFile, "cmake_autorcc", commandLines);
std::string const ccComment =
cmStrCat("Automatic RCC for ",
@ -1617,13 +1723,28 @@ bool cmQtAutoGenInitializer::InitRccTargets()
// Create custom rcc command
{
// Add the resource files to the dependencies
for (std::string const& fileName : qrc.Resources) {
// Add resource file to the custom command dependencies
ccDepends.push_back(fileName);
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
// Add resource file to the custom command dependencies
auto resourceFilesWithConfig = cmStrCat(
"$<$<CONFIG:", config,
">:", cmList{ qrc.Resources.Config.at(config) }.to_string(),
">");
ccDepends.emplace_back(std::move(resourceFilesWithConfig));
}
} else {
for (std::string const& fileName : qrc.Resources.Default) {
// Add resource file to the custom command dependencies
ccDepends.push_back(fileName);
}
}
if (!this->Rcc.ExecutableTargetName.empty()) {
ccDepends.push_back(this->Rcc.ExecutableTargetName);
}
AddAutogenExecutableToDependencies(this->Rcc, ccDepends);
cc->SetOutputs(ccOutput);
cc->SetDepends(ccDepends);
this->LocalGen->AddCustomCommandToOutput(std::move(cc));
@ -1723,6 +1844,8 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
// General
info.SetBool("MULTI_CONFIG", this->MultiConfig);
info.SetBool("CROSS_CONFIG", this->CrossConfig);
info.SetBool("USE_BETTER_GRAPH", this->UseBetterGraph);
info.SetUInt("PARALLEL", this->AutogenTarget.Parallel);
#ifdef _WIN32
info.SetUInt("AUTOGEN_COMMAND_LINE_LENGTH_MAX",
@ -1740,14 +1863,14 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
info.SetUInt("QT_VERSION_MAJOR", this->QtVersion.Major);
info.SetUInt("QT_VERSION_MINOR", this->QtVersion.Minor);
info.Set("QT_MOC_EXECUTABLE", this->Moc.Executable);
info.Set("QT_UIC_EXECUTABLE", this->Uic.Executable);
info.SetConfig("QT_MOC_EXECUTABLE", this->Moc.Executable);
info.SetConfig("QT_UIC_EXECUTABLE", this->Uic.Executable);
info.Set("CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand());
info.SetConfig("SETTINGS_FILE", this->AutogenTarget.SettingsFile);
info.SetConfig("PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile);
info.Set("DEP_FILE", this->AutogenTarget.DepFile);
info.Set("DEP_FILE_RULE_NAME", this->AutogenTarget.DepFileRuleName);
info.SetConfig("DEP_FILE", this->AutogenTarget.DepFile);
info.SetConfig("DEP_FILE_RULE_NAME", this->AutogenTarget.DepFileRuleName);
info.SetArray("CMAKE_LIST_FILES", this->Makefile->GetListFiles());
info.SetArray("HEADER_EXTENSIONS",
this->Makefile->GetCMakeInstance()->GetHeaderExtensions());
@ -1874,6 +1997,8 @@ bool cmQtAutoGenInitializer::SetupWriteRccInfo()
// General
info.SetBool("MULTI_CONFIG", this->MultiConfig);
info.SetBool("CROSS_CONFIG", this->CrossConfig);
info.SetBool("USE_BETTER_GRAPH", this->UseBetterGraph);
info.SetUInt("VERBOSITY", this->Verbosity);
info.Set("GENERATOR", this->GlobalGen->GetName());
@ -1890,16 +2015,17 @@ bool cmQtAutoGenInitializer::SetupWriteRccInfo()
info.SetConfig("INCLUDE_DIR", this->Dir.Include);
// rcc executable
info.Set("RCC_EXECUTABLE", this->Rcc.Executable);
info.SetArray("RCC_LIST_OPTIONS",
this->Rcc.ExecutableFeatures->ListOptions);
info.SetConfig("RCC_EXECUTABLE", this->Rcc.Executable);
info.SetConfigArray(
"RCC_LIST_OPTIONS",
generateListOptions(this->Rcc.ExecutableFeatures, this->MultiConfig));
// qrc file
info.Set("SOURCE", qrc.QrcFile);
info.Set("OUTPUT_CHECKSUM", qrc.QrcPathChecksum);
info.Set("OUTPUT_NAME", cmSystemTools::GetFilenameName(qrc.OutputFile));
info.SetArray("OPTIONS", qrc.Options);
info.SetArray("INPUTS", qrc.Resources);
info.SetConfigArray("INPUTS", qrc.Resources);
info.Save(qrc.InfoFile);
}
@ -2244,16 +2370,32 @@ bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars,
cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
cmGeneratorExpression ge(*this->Makefile->GetCMakeInstance(), lfbt);
std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(val);
genVars.Executable = cge->Evaluate(this->LocalGen, "");
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
genVars.Executable.Config[config] =
cge->Evaluate(this->LocalGen, config);
}
} else {
genVars.Executable.Default = cge->Evaluate(this->LocalGen, "");
}
}
if (genVars.Executable.empty() && !ignoreMissingTarget) {
if (genVars.Executable.Default.empty() &&
genVars.Executable.Config.empty() && !ignoreMissingTarget) {
print_err(prop + " evaluates to an empty value");
return false;
}
// Create empty compiler features.
genVars.ExecutableFeatures =
std::make_shared<cmQtAutoGen::CompilerFeatures>();
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
genVars.ExecutableFeatures.Config[config] =
std::make_shared<cmQtAutoGen::CompilerFeatures>();
}
} else {
genVars.ExecutableFeatures.Default =
std::make_shared<cmQtAutoGen::CompilerFeatures>();
}
return true;
}
}
@ -2278,15 +2420,39 @@ bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars,
genVars.ExecutableTargetName = targetName;
genVars.ExecutableTarget = genTarget;
if (genTarget->IsImported()) {
genVars.Executable = genTarget->ImportedGetLocation("");
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
genVars.Executable.Config[config] =
genTarget->ImportedGetLocation(config);
}
} else {
genVars.Executable.Default =
genTarget->ImportedGetLocation(this->ConfigDefault);
}
} else {
genVars.Executable = genTarget->GetLocation("");
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
genVars.Executable.Config[config] = genTarget->GetLocation(config);
}
} else {
genVars.Executable.Default =
genTarget->GetLocation(this->ConfigDefault);
}
}
} else {
if (ignoreMissingTarget) {
// Create empty compiler features.
genVars.ExecutableFeatures =
std::make_shared<cmQtAutoGen::CompilerFeatures>();
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
genVars.ExecutableFeatures.Config[config] =
std::make_shared<cmQtAutoGen::CompilerFeatures>();
}
} else {
genVars.ExecutableFeatures.Default =
std::make_shared<cmQtAutoGen::CompilerFeatures>();
}
return true;
}
print_err(cmStrCat("Could not find ", executable, " executable target ",
@ -2299,10 +2465,22 @@ bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars,
{
std::string err;
genVars.ExecutableFeatures = this->GlobalInitializer->GetCompilerFeatures(
executable, genVars.Executable, err);
if (!genVars.ExecutableFeatures) {
print_err(err);
return false;
executable, genVars.Executable, err, this->MultiConfig,
this->UseBetterGraph);
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
if (!genVars.ExecutableFeatures.Config[config]) {
if (!genVars.ExecutableFeatures.Config[config]) {
print_err(err);
return false;
}
}
}
} else {
if (!genVars.ExecutableFeatures.Default) {
print_err(err);
return false;
}
}
}

View File

@ -19,6 +19,7 @@
#include "cmFilePathChecksum.h"
#include "cmQtAutoGen.h"
class cmCustomCommandLines;
class cmGeneratorTarget;
class cmGlobalGenerator;
class cmLocalGenerator;
@ -33,23 +34,6 @@ class cmTarget;
class cmQtAutoGenInitializer : public cmQtAutoGen
{
public:
/** String value with per configuration variants. */
class ConfigString
{
public:
std::string Default;
std::unordered_map<std::string, std::string> Config;
};
/** String values with per configuration variants. */
template <typename C>
class ConfigStrings
{
public:
C Default;
std::unordered_map<std::string, C> Config;
};
/** rcc job. */
class Qrc
{
@ -64,7 +48,7 @@ public:
bool Generated = false;
bool Unique = false;
std::vector<std::string> Options;
std::vector<std::string> Resources;
ConfigStrings<std::vector<std::string>> Resources;
};
/** moc and/or uic file. */
@ -91,8 +75,8 @@ public:
// Executable
std::string ExecutableTargetName;
cmGeneratorTarget* ExecutableTarget = nullptr;
std::string Executable;
CompilerFeaturesHandle ExecutableFeatures;
ConfigString Executable;
ConfigStrings<CompilerFeaturesHandle> ExecutableFeatures;
GenVarsT(GenT gen)
: Gen(gen)
@ -142,6 +126,9 @@ private:
GenVarsT const& genVars, bool prepend = false);
void AddToSourceGroup(std::string const& fileName,
cm::string_view genNameUpper);
void AddCMakeProcessToCommandLines(std::string const& infoFile,
std::string const& processName,
cmCustomCommandLines& commandLines);
void AddCleanFile(std::string const& fileName);
void ConfigFileNames(ConfigString& configString, cm::string_view prefix,
@ -156,6 +143,9 @@ private:
bool ignoreMissingTarget) const;
void handleSkipPch(cmSourceFile* sf);
void AddAutogenExecutableToDependencies(
cmQtAutoGenInitializer::GenVarsT const& genVars,
std::vector<std::string>& dependencies) const;
cmQtAutoGenGlobalInitializer* GlobalInitializer = nullptr;
cmGeneratorTarget* GenTarget = nullptr;
@ -169,6 +159,7 @@ private:
unsigned int Verbosity = 0;
bool MultiConfig = false;
bool CrossConfig = false;
bool UseBetterGraph = false;
bool CMP0071Accept = false;
bool CMP0071Warn = false;
bool CMP0100Accept = false;
@ -205,8 +196,8 @@ private:
bool DependOrigin = false;
std::set<std::string> DependFiles;
std::set<cmTarget*> DependTargets;
std::string DepFile;
std::string DepFileRuleName;
ConfigString DepFile;
ConfigString DepFileRuleName;
// Sources to process
std::unordered_map<cmSourceFile*, MUFileHandle> Headers;
std::unordered_map<cmSourceFile*, MUFileHandle> Sources;

View File

@ -430,10 +430,12 @@ std::string cmQtAutoGenerator::MessagePath(cm::string_view path) const
return cmQtAutoGen::Quoted(res);
}
bool cmQtAutoGenerator::Run(cm::string_view infoFile, cm::string_view config)
bool cmQtAutoGenerator::Run(cm::string_view infoFile, cm::string_view config,
cm::string_view executableConfig)
{
// Info config
this->InfoConfig_ = std::string(config);
this->ExecutableConfig_ = std::string(executableConfig);
// Info file
this->InfoFile_ = std::string(infoFile);

View File

@ -90,6 +90,10 @@ public:
std::string const& InfoDir() const { return this->InfoDir_; }
cmFileTime const& InfoFileTime() const { return this->InfoFileTime_; }
std::string const& InfoConfig() const { return this->InfoConfig_; }
std::string const& ExecutableConfig() const
{
return this->ExecutableConfig_;
}
// -- Info file parsing
/** Info file reader class. */
@ -151,7 +155,8 @@ public:
std::string MessagePath(cm::string_view path) const;
// -- Run
bool Run(cm::string_view infoFile, cm::string_view config);
bool Run(cm::string_view infoFile, cm::string_view config,
cm::string_view executableConfig);
protected:
// -- Abstract processing interface
@ -170,6 +175,7 @@ private:
std::string InfoDir_;
cmFileTime InfoFileTime_;
std::string InfoConfig_;
std::string ExecutableConfig_;
// -- Directories
ProjectDirsT ProjectDirs_;
};

View File

@ -171,6 +171,8 @@ public:
// -- Attributes
// - Config
bool MultiConfig = false;
bool CrossConfig = false;
bool UseBetterGraph = false;
IntegerVersion QtVersion = { 4, 0 };
unsigned int ThreadCount = 0;
unsigned int MaxCommandLineLength =
@ -2380,6 +2382,9 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
{
// -- Required settings
if (!info.GetBool("MULTI_CONFIG", this->BaseConst_.MultiConfig, true) ||
!info.GetBool("CROSS_CONFIG", this->BaseConst_.CrossConfig, true) ||
!info.GetBool("USE_BETTER_GRAPH", this->BaseConst_.UseBetterGraph,
true) ||
!info.GetUInt("QT_VERSION_MAJOR", this->BaseConst_.QtVersion.Major,
true) ||
!info.GetUInt("QT_VERSION_MINOR", this->BaseConst_.QtVersion.Minor,
@ -2396,19 +2401,49 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
true) ||
!info.GetStringConfig("PARSE_CACHE_FILE",
this->BaseConst_.ParseCacheFile, true) ||
!info.GetString("DEP_FILE", this->BaseConst_.DepFile, false) ||
!info.GetString("DEP_FILE_RULE_NAME", this->BaseConst_.DepFileRuleName,
false) ||
!info.GetStringConfig("SETTINGS_FILE", this->SettingsFile_, true) ||
!info.GetArray("CMAKE_LIST_FILES", this->BaseConst_.ListFiles, true) ||
!info.GetArray("HEADER_EXTENSIONS", this->BaseConst_.HeaderExtensions,
true) ||
!info.GetString("QT_MOC_EXECUTABLE", this->MocConst_.Executable,
false) ||
!info.GetString("QT_UIC_EXECUTABLE", this->UicConst_.Executable,
false)) {
true)) {
return false;
}
if (this->BaseConst().UseBetterGraph) {
if (!info.GetStringConfig("DEP_FILE", this->BaseConst_.DepFile, false) ||
!info.GetStringConfig("DEP_FILE_RULE_NAME",
this->BaseConst_.DepFileRuleName, false)) {
return false;
}
if (this->BaseConst_.CrossConfig) {
std::string const mocExecutableWithConfig =
"QT_MOC_EXECUTABLE_" + this->ExecutableConfig();
std::string const uicExecutableWithConfig =
"QT_UIC_EXECUTABLE_" + this->ExecutableConfig();
if (!info.GetString(mocExecutableWithConfig, this->MocConst_.Executable,
false) ||
!info.GetString(uicExecutableWithConfig, this->UicConst_.Executable,
false)) {
return false;
}
} else {
if (!info.GetStringConfig("QT_MOC_EXECUTABLE",
this->MocConst_.Executable, false) ||
!info.GetStringConfig("QT_UIC_EXECUTABLE",
this->UicConst_.Executable, false)) {
return false;
}
}
} else {
if (!info.GetString("QT_MOC_EXECUTABLE", this->MocConst_.Executable,
false) ||
!info.GetString("QT_UIC_EXECUTABLE", this->UicConst_.Executable,
false) ||
!info.GetString("DEP_FILE", this->BaseConst_.DepFile, false) ||
!info.GetString("DEP_FILE_RULE_NAME", this->BaseConst_.DepFileRuleName,
false)) {
return false;
}
}
// -- Checks
if (!this->BaseConst_.CMakeExecutableTime.Load(
@ -3075,7 +3110,8 @@ std::string cmQtAutoMocUicT::AbsoluteIncludePath(
} // End of unnamed namespace
bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config)
bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config,
cm::string_view executableConfig)
{
return cmQtAutoMocUicT().Run(infoFile, config);
return cmQtAutoMocUicT().Run(infoFile, config, executableConfig);
}

View File

@ -10,4 +10,5 @@
* Process AUTOMOC and AUTOUIC
* @return true on success
*/
bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config);
bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config,
cm::string_view executableConfig);

View File

@ -35,6 +35,11 @@ public:
private:
// -- Utility
bool IsMultiConfig() const { return this->MultiConfig_; }
std::string const& GetGenerator() const { return this->Generator_; }
bool IsXcode() const
{
return this->GetGenerator().find("Xcode") != std::string::npos;
}
std::string MultiConfigOutput() const;
// -- Abstract processing interface
@ -53,6 +58,9 @@ private:
// -- Config settings
bool MultiConfig_ = false;
bool CrossConfig_ = false;
bool UseBetterGraph_ = false;
std::string Generator_;
// -- Directories
std::string AutogenBuildDir_;
std::string IncludeDir_;
@ -92,26 +100,57 @@ bool cmQtAutoRccT::InitFromInfo(InfoT const& info)
{
// -- Required settings
if (!info.GetBool("MULTI_CONFIG", this->MultiConfig_, true) ||
!info.GetString("GENERATOR", this->Generator_, true) ||
!info.GetBool("CROSS_CONFIG", this->CrossConfig_, true) ||
!info.GetBool("USE_BETTER_GRAPH", this->UseBetterGraph_, true) ||
!info.GetString("BUILD_DIR", this->AutogenBuildDir_, true) ||
!info.GetStringConfig("INCLUDE_DIR", this->IncludeDir_, true) ||
!info.GetString("RCC_EXECUTABLE", this->RccExecutable_, true) ||
!info.GetArray("RCC_LIST_OPTIONS", this->RccListOptions_, false) ||
!info.GetArrayConfig("RCC_LIST_OPTIONS", this->RccListOptions_, false) ||
!info.GetString("LOCK_FILE", this->LockFile_, true) ||
!info.GetStringConfig("SETTINGS_FILE", this->SettingsFile_, true) ||
!info.GetString("SOURCE", this->QrcFile_, true) ||
!info.GetString("OUTPUT_CHECKSUM", this->RccPathChecksum_, true) ||
!info.GetString("OUTPUT_NAME", this->RccFileName_, true) ||
!info.GetArray("OPTIONS", this->Options_, false) ||
!info.GetArray("INPUTS", this->Inputs_, false)) {
!info.GetArray("OPTIONS", this->Options_, false)) {
return false;
}
if (this->UseBetterGraph_) {
if (!info.GetArrayConfig("INPUTS", this->Inputs_, false)) {
return false;
}
if (this->CrossConfig_) {
std::string const rccExecutableWithConfig =
"RCC_EXECUTABLE_" + this->ExecutableConfig();
if (!info.GetString(rccExecutableWithConfig, this->RccExecutable_,
true)) {
return false;
}
} else {
if (!info.GetStringConfig("RCC_EXECUTABLE", this->RccExecutable_,
true)) {
return false;
}
}
} else {
if (!info.GetString("RCC_EXECUTABLE", this->RccExecutable_, true) ||
!info.GetArray("RCC_LIST_OPTIONS", this->RccListOptions_, false) ||
!info.GetArray("INPUTS", this->Inputs_, false)) {
return false;
}
}
// -- Derive information
this->QrcFileName_ = cmSystemTools::GetFilenameName(this->QrcFile_);
this->QrcFileDir_ = cmSystemTools::GetFilenamePath(this->QrcFile_);
this->RccFilePublic_ =
cmStrCat(this->AutogenBuildDir_, '/', this->RccPathChecksum_, '/',
this->RccFileName_);
if (IsMultiConfig() && !this->IsXcode() && this->UseBetterGraph_) {
this->RccFilePublic_ =
cmStrCat(this->AutogenBuildDir_, '/', this->RccPathChecksum_, "_",
this->InfoConfig(), '/', this->RccFileName_);
} else {
this->RccFilePublic_ =
cmStrCat(this->AutogenBuildDir_, '/', this->RccPathChecksum_, '/',
this->RccFileName_);
}
// rcc output file name
if (this->IsMultiConfig()) {
@ -520,7 +559,8 @@ bool cmQtAutoRccT::GenerateWrapper()
} // End of unnamed namespace
bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config)
bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config,
cm::string_view executableConfig)
{
return cmQtAutoRccT().Run(infoFile, config);
return cmQtAutoRccT().Run(infoFile, config, executableConfig);
}

View File

@ -10,4 +10,5 @@
* Process AUTORCC
* @return true on success
*/
bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config);
bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config,
cm::string_view executableConfig);

View File

@ -556,6 +556,7 @@ TargetProperty const StaticTargetProperties[] = {
{ "AUTOGEN_ORIGIN_DEPENDS"_s, IC::CanCompileSources },
{ "AUTOGEN_PARALLEL"_s, IC::CanCompileSources },
{ "AUTOGEN_USE_SYSTEM_INCLUDE"_s, IC::CanCompileSources },
{ "AUTOGEN_BETTER_GRAPH_MULTI_CONFIG"_s, IC::CanCompileSources },
// -- moc
{ "AUTOMOC_DEPEND_FILTERS"_s, IC::CanCompileSources },
// -- C++

View File

@ -1443,13 +1443,17 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
if ((args[1] == "cmake_autogen") && (args.size() >= 4)) {
cm::string_view const infoFile = args[2];
cm::string_view const config = args[3];
return cmQtAutoMocUic(infoFile, config) ? 0 : 1;
cm::string_view const executableConfig =
(args.size() >= 5) ? cm::string_view(args[4]) : cm::string_view();
return cmQtAutoMocUic(infoFile, config, executableConfig) ? 0 : 1;
}
if ((args[1] == "cmake_autorcc") && (args.size() >= 3)) {
cm::string_view const infoFile = args[2];
cm::string_view const config =
(args.size() > 3) ? cm::string_view(args[3]) : cm::string_view();
return cmQtAutoRcc(infoFile, config) ? 0 : 1;
cm::string_view const executableConfig =
(args.size() >= 5) ? cm::string_view(args[4]) : cm::string_view();
return cmQtAutoRcc(infoFile, config, executableConfig) ? 0 : 1;
}
#endif

View File

@ -128,65 +128,339 @@ if (DEFINED with_qt_version)
if(QtCore_VERSION VERSION_GREATER_EQUAL 5.15.0)
if (RunCMake_GENERATOR MATCHES "Ninja Multi-Config")
set(config_list Debug Release RelWithDebInfo)
set(use_better_graph_list ON OFF)
else()
set(config_list single-config)
set(use_better_graph_list OFF)
endif()
foreach(config IN ITEMS ${config_list})
block()
if (config STREQUAL "single-config")
set(config_suffix "")
else()
set(config_suffix "_${config}")
endif()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocDeps${config_suffix}-build)
run_cmake(QtAutoMocDeps)
set(RunCMake_TEST_NO_CLEAN 1)
# Build the project.
if (config STREQUAL "single-config")
set(config_param "")
else()
set(config_param "--config ${config}")
endif()
run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose ${config_param})
# Touch just the library source file, which shouldn't cause a rerun of AUTOMOC
# for app_with_qt target.
file(TOUCH "${RunCMake_SOURCE_DIR}/simple_lib.cpp")
# Build and assert that AUTOMOC was not run for app_with_qt, sub_exe_1 and sub_exe_2.
run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose ${config_param})
unset(RunCMake_TEST_VARIANT_DESCRIPTION)
unset(RunCMake_TEST_NOT_EXPECT_stdout)
macro(check_file_exists file)
if (EXISTS "${file}")
set(check_result "PASSED")
set(message_type "STATUS")
foreach(use_better_graph IN ITEMS ${use_better_graph_list})
foreach(config IN ITEMS ${config_list})
block()
if (config STREQUAL "single-config")
set(config_suffix "")
else()
set(check_result "FAILED")
set(message_type "FATAL_ERROR")
set(config_path "_${config}")
if (use_better_graph)
set(config_suffix "_${config}")
endif()
endif()
message(${message_type} "QtAutoMocDeps-build-\"${file}\" was generated - ${check_result}")
endmacro()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocDeps${config_path}-build)
run_cmake_with_options(QtAutoMocDeps ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=${use_better_graph})
set(RunCMake_TEST_NO_CLEAN 1)
# Build the project.
if (config STREQUAL "single-config")
set(config_param "")
else()
set(config_param "--config ${config}")
endif()
run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose ${config_param})
# Touch just the library source file, which shouldn't cause a rerun of AUTOMOC
# for app_with_qt target.
file(TOUCH "${RunCMake_SOURCE_DIR}/simple_lib.cpp")
set(RunCMake_TEST_NOT_EXPECT_stdout "Automatic MOC for target app_with_qt|\
Automatic MOC for target sub_exe_1|\
Automatic MOC for target sub_exe_2")
set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't execute AUTOMOC for 'app_with_qt', 'sub_exe_1' and 'sub_exe_2'")
# Build and assert that AUTOMOC was not run for app_with_qt, sub_exe_1 and sub_exe_2.
run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose ${config_param})
unset(RunCMake_TEST_VARIANT_DESCRIPTION)
unset(RunCMake_TEST_NOT_EXPECT_stdout)
check_file_exists("${RunCMake_TEST_BINARY_DIR}/app_with_qt_autogen/deps")
check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir1/sub_exe_1_autogen/deps")
check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir2/sub_exe_2_autogen/deps")
macro(check_file_exists file)
if (EXISTS "${file}")
set(check_result "PASSED")
set(message_type "STATUS")
else()
set(check_result "FAILED")
set(message_type "FATAL_ERROR")
endif()
check_file_exists("${RunCMake_TEST_BINARY_DIR}/app_with_qt_autogen/timestamp")
check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir1/sub_exe_1_autogen/timestamp")
check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir2/sub_exe_2_autogen/timestamp")
message(${message_type} "QtAutoMocDeps-build-\"${file}\" was generated - ${check_result}")
endmacro()
# Touch a header file to make sure an automoc dependency cycle is not introduced.
file(TOUCH "${RunCMake_SOURCE_DIR}/MyWindow.h")
set(RunCMake_TEST_VARIANT_DESCRIPTION "-First build after touch to detect dependency cycle")
run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose)
# Need to run a second time to hit the dependency cycle.
set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't hit dependency cycle")
run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose)
endblock()
check_file_exists("${RunCMake_TEST_BINARY_DIR}/app_with_qt_autogen/deps${config_suffix}")
check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir1/sub_exe_1_autogen/deps${config_suffix}")
check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir2/sub_exe_2_autogen/deps${config_suffix}")
check_file_exists("${RunCMake_TEST_BINARY_DIR}/app_with_qt_autogen/timestamp${config_suffix}")
check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir1/sub_exe_1_autogen/timestamp${config_suffix}")
check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir2/sub_exe_2_autogen/timestamp${config_suffix}")
# Touch a header file to make sure an automoc dependency cycle is not introduced.
file(TOUCH "${RunCMake_SOURCE_DIR}/MyWindow.h")
set(RunCMake_TEST_VARIANT_DESCRIPTION "-First build after touch to detect dependency cycle")
run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose)
# Need to run a second time to hit the dependency cycle.
set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't hit dependency cycle")
run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose)
endblock()
endforeach()
endforeach()
endif()
endblock()
endif()
function(run_make_program dir)
execute_process(
COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN}
WORKING_DIRECTORY "${dir}"
OUTPUT_VARIABLE make_program_stdout
ERROR_VARIABLE make_program_stderr
RESULT_VARIABLE make_program_result
)
if (NOT DEFINED RunMakeProgram_expected_result)
set(RunMakeProgram_expected_result 0)
endif()
if(NOT "${make_program_result}" MATCHES "${RunMakeProgram_expected_result}")
message(STATUS "
============ beginning of ${RunCMake_MAKE_PROGRAM}'s stdout ============
${make_program_stdout}
=============== end of ${RunCMake_MAKE_PROGRAM}'s stdout ===============
")
message(STATUS "
============ beginning of ${RunCMake_MAKE_PROGRAM}'s stderr ============
${make_program_stderr}
=============== end of ${RunCMake_MAKE_PROGRAM}'s stderr ===============
")
message(FATAL_ERROR
"top ${RunCMake_MAKE_PROGRAM} build failed exited with status ${make_program_result}")
endif()
set(make_program_stdout "${make_program_stdout}" PARENT_SCOPE)
endfunction(run_make_program)
function(count_substring STRING SUBSTRING COUNT_VAR)
string(LENGTH "${STRING}" STRING_LENGTH)
string(LENGTH "${SUBSTRING}" SUBSTRING_LENGTH)
if (SUBSTRING_LENGTH EQUAL 0)
message(FATAL_ERROR "SUBSTRING_LENGTH is 0")
endif()
if (STRING_LENGTH EQUAL 0)
message(FATAL_ERROR "STRING_LENGTH is 0")
endif()
if (STRING_LENGTH LESS SUBSTRING_LENGTH)
message(FATAL_ERROR "STRING_LENGTH is less than SUBSTRING_LENGTH")
endif()
set(COUNT 0)
string(FIND "${STRING}" "${SUBSTRING}" SUBSTRING_START)
while(SUBSTRING_START GREATER_EQUAL 0)
math(EXPR COUNT "${COUNT} + 1")
math(EXPR SUBSTRING_START "${SUBSTRING_START} + ${SUBSTRING_LENGTH}")
string(SUBSTRING "${STRING}" ${SUBSTRING_START} -1 STRING)
string(FIND "${STRING}" "${SUBSTRING}" SUBSTRING_START)
endwhile()
set(${COUNT_VAR} ${COUNT} PARENT_SCOPE)
endfunction()
function(expect_only_once make_program_stdout expected_output test_name)
count_substring("${make_program_stdout}" "${expected_output}" count)
if(NOT count EQUAL 1)
message(STATUS "${test_name}-expect_only_once - FAILED")
message(FATAL_ERROR "Expected to find ${expected_output} exactly once in ${make_program_stdout} but found ${count} occurrences of ${expected_output}")
else()
message(STATUS "${test_name}-expect_only_once - PASSED")
endif()
endfunction()
function(expect_n_times string_to_check expected_output expected_count test_name)
count_substring("${string_to_check}" "${expected_output}" count)
if(NOT count EQUAL ${expected_count})
message(STATUS "${test_name}-expect_${expected_count}_times - FAILED")
message(FATAL_ERROR "Expected to find ${expected_output} exactly ${expected_count} times in ${string_to_check} but found ${count} occurrences of ${expected_output}")
else()
message(STATUS "${test_name}-expect_${expected_count}_times - PASSED")
endif()
endfunction()
function(not_expect make_program_stdout unexpected_output test_name)
count_substring("${make_program_stdout}" "${unexpected_output}" count)
if(NOT count EQUAL 0)
message(STATUS "${test_name}-not_expect - FAILED")
message(FATAL_ERROR "Expected to find ${unexpected_output} exactly 0 times in ${make_program_stdout} but found ${count} occurrences of ${unexpected_output}")
else()
message(STATUS "${test_name}-not_expect - PASSED")
endif()
endfunction()
if (QtCore_VERSION VERSION_GREATER_EQUAL 5.15.0)
foreach(exe IN ITEMS Moc Uic Rcc)
if(RunCMake_GENERATOR MATCHES "Ninja Multi-Config")
block()
set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure")
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-multi-config-build)
run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON)
unset(RunCMake_TEST_VARIANT_DESCRIPTION)
set(RunCMake_TEST_NO_CLEAN 1)
foreach(config IN ITEMS Debug Release RelWithDebInfo)
block()
set(RunCMake_TEST_EXPECT_stdout ".*running_exe_${config}*")
set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-expect_running_exe_${config}")
run_cmake_command(Auto${exe}ExecutableConfig-multi-config-build ${CMAKE_COMMAND} --build . --config ${config})
endblock()
endforeach()
set(RunCMake_TEST_EXPECT_stdout "ninja: no work to do")
foreach(config IN ITEMS Debug Release RelWithDebInfo)
block()
set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-expect_no_work_to_do")
run_cmake_command(Auto${exe}ExecutableConfig-multi-config-build ${CMAKE_COMMAND} --build . --config ${config})
endblock()
endforeach()
endblock()
block()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-build)
run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON)
foreach(config IN ITEMS Debug Release RelWithDebInfo)
block()
run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${config}.ninja)
set(expected_output "running_exe_${config}")
expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig-${config}-${expected_output}")
foreach(sub_config IN ITEMS Debug Release RelWithDebInfo)
if(NOT sub_config STREQUAL config)
set(unexpected_output "running_exe_${sub_config}")
not_expect("${make_program_stdout}" "${unexpected_output}" "Auto${exe}ExecutableConfig-${config}-${unexpected_output}")
endif()
endforeach()
if (exe STREQUAL "Moc" OR exe STREQUAL "Uic")
set(expected_output "cmake_autogen")
else()
set(expected_output "cmake_autorcc")
endif()
expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig-${config}-${expected_output}")
endblock()
endforeach()
endblock()
block()
foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo)
foreach(target_config IN ITEMS Debug Release RelWithDebInfo)
block()
set(TEST_SUFFIX "-CrossConfig-${ninja_config}-${target_config}")
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig${TEST_SUFFIX}-build)
set(RunCMake_TEST_VARIANT_DESCRIPTION ${TEST_SUFFIX})
run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_CROSS_CONFIGS=all -DCMAKE_DEFAULT_BUILD_TYPE=${ninja_config} -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON)
unset(RunCMake_TEST_VARIANT_DESCRIPTION)
run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${ninja_config}.ninja dummy:${target_config})
set(expected_output "running_exe_${ninja_config}")
expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${expected_output}")
foreach(sub_config IN ITEMS Debug Release RelWithDebInfo)
if(NOT sub_config STREQUAL ninja_config)
set(unexpected_output "running_exe_${sub_config}")
not_expect("${make_program_stdout}" "${unexpected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${unexpected_output}")
endif()
endforeach()
if (exe STREQUAL "Moc" OR exe STREQUAL "Uic")
set(expected_output "cmake_autogen")
else()
set(expected_output "cmake_autorcc")
endif()
expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${expected_output}")
endblock()
endforeach()
endforeach()
endblock()
block()
foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo)
set(TEST_SUFFIX "-CrossConfig-${ninja_config}-all-all")
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig${TEST_SUFFIX}-build)
set(RunCMake_TEST_VARIANT_DESCRIPTION ${TEST_SUFFIX})
run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_CROSS_CONFIGS=all -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON)
unset(RunCMake_TEST_VARIANT_DESCRIPTION)
run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${ninja_config}.ninja all:all)
endforeach()
endblock()
elseif (RunCMake_GENERATOR MATCHES "Ninja|Make")
block()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-build)
foreach(config IN ITEMS Debug Release RelWithDebInfo)
block()
set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}")
run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_BUILD_TYPE=${config} -DCMAKE_AUTOGEN_VERBOSE=ON)
unset(RunCMake_TEST_VARIANT_DESCRIPTION)
set(RunCMake_TEST_NO_CLEAN 1)
set(RunCMake_TEST_EXPECT_stdout ".*running_exe_${config}*")
run_cmake_command(Auto${exe}ExecutableConfig-${config}-build ${CMAKE_COMMAND} --build .)
endblock()
endforeach()
endblock()
endif()
endforeach()
endif()
# Visual Studio specific dependency tests
if (RunCMake_GENERATOR MATCHES "Visual Studio")
foreach(exe IN ITEMS Moc Uic Rcc)
block()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${exe}Example-build)
set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure")
run_cmake_with_options(${exe}Example ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON)
unset(RunCMake_TEST_VARIANT_DESCRIPTION)
set(RunCMake_TEST_NO_CLEAN 1)
foreach(config IN ITEMS Debug Release RelWithDebInfo)
block()
set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-first-build")
run_cmake_command(${exe}Example-build ${CMAKE_COMMAND} --build . --config ${config})
endblock()
endforeach()
foreach(config IN ITEMS Debug Release RelWithDebInfo)
block()
if (exe STREQUAL "Moc" OR exe STREQUAL "Uic")
set(RunCMake_TEST_NOT_EXPECT_stdout "Auto${exe}")
set(not_expect_descripton "Auto${exe}")
else ()
set(RunCMake_TEST_NOT_EXPECT_stdout "Auto${exe}")
set(not_expect_descripton "Auto${exe}")
endif()
set(RunCMake_TEST_VARIANT_DESCRIPTION "-second-build-${config}_expect_no_${not_expect_descripton}")
run_cmake_command(${exe}Example-build ${CMAKE_COMMAND} --build . --config ${config})
endblock()
endforeach()
endblock()
endforeach()
endif()
if (RunCMake_GENERATOR MATCHES "Xcode")
foreach(exe IN ITEMS Moc Uic Rcc)
block()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${exe}Example-build)
set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure")
set(RunCMake_TEST_EXPECT_stderr ".*")
run_cmake_with_options(${exe}Example ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON)
set(RunCMake_TEST_NO_CLEAN 1)
set(RunCMake_MAKE_PROGRAM ${CMAKE_COMMAND})
run_make_program(${RunCMake_TEST_BINARY_DIR} --build . --config Debug)
if (exe STREQUAL "Moc")
set(expected_count 16)
elseif (exe STREQUAL "Uic")
set(expected_count 4)
else()
set(expected_count 12)
endif()
expect_n_times("${make_program_stdout}" "Auto${exe}:" ${expected_count} "${exe}Example-build-Auto${exe}")
expect_n_times("${make_program_stdout}" "Auto${exe}:" ${expected_count} "${exe}Example-build-Auto${exe}")
if (exe STREQUAL "Moc" OR exe STREQUAL "Uic")
expect_n_times("${make_program_stdout}" "AutoGen:" 20 "${exe}Example-build-AutoGen:")
endif()
foreach(config IN ITEMS Debug Release RelWithDebInfo)
block()
run_make_program(${RunCMake_TEST_BINARY_DIR} --build . --config ${config})
not_expect("${make_program_stdout}" "Auto${exe}" "${exe}Example-${config}_Auto${exe}")
not_expect("${make_program_stdout}" "AutoGen:" "${exe}Example-${config}_AutoGen")
endblock()
endforeach()
endblock()
endforeach()
endif()
endif ()

View File

@ -0,0 +1,4 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/res/affine">
</qresource>
</RCC>

View File

@ -0,0 +1,5 @@
#include "example.h"
Example::Example()
{
}

View File

@ -0,0 +1,12 @@
#ifndef EXAMPLE_H
#define EXAMPLE_H
#include <QObject>
class Example : public QObject
{
Q_OBJECT
Example();
};
#endif

View File

@ -0,0 +1,5 @@
#include "example_ui.h"
Example::Example()
{
}

View File

@ -0,0 +1,14 @@
#ifndef EXAMPLE_UI_H
#define EXAMPLE_UI_H
#include <QObject>
#include "ui_uiA.h"
class Example : public QObject
{
Q_OBJECT
Example();
};
#endif

View File

@ -0,0 +1,4 @@
int main()
{
return 0;
}

View File

@ -0,0 +1,48 @@
#ifndef EXE_COMMON_H
#define EXE_COMMON_H
#include <cstdlib>
#include <fstream>
#include <string>
#include <vector>
inline int runRealExe(const int argc, char** argv)
{
std::vector<std::string> args;
std::string realMocPath;
std::string const pathArg = "EXE_PATH=";
std::string cmd;
if (argc > 1) {
for (int i = 1; i < argc; ++i) {
std::string const arg = argv[i];
if (arg.find(pathArg) != std::string::npos) {
realMocPath = arg.substr(pathArg.length());
// if EXE_PATH contains spaces, wrap it in quotes
if (realMocPath.find(" ") != std::string::npos) {
realMocPath = "\"" + realMocPath + "\"";
}
} else {
args.push_back(arg);
}
}
}
#ifdef _WIN32
cmd += "cmd /C \"";
#endif
cmd += realMocPath + " ";
for (auto arg : args) {
// if arg contains spaces, wrap it in quotes
if (arg.find(' ') != std::string::npos) {
cmd += " \"" + arg + "\"";
} else {
cmd += " " + arg;
}
}
#ifdef _WIN32
cmd += "\"";
#endif
std::cout << "Running real exe:" << cmd << std::endl;
return std::system(cmd.c_str());
}
#endif

View File

@ -0,0 +1,10 @@
#include <fstream>
#include <iostream>
#include "exe_common.h"
int main(int argc, char* argv[])
{
std::cout << "running_exe_Debug\n";
return runRealExe(argc, argv);
}

View File

@ -0,0 +1,10 @@
#include <fstream>
#include <iostream>
#include "exe_common.h"
int main(int argc, char* argv[])
{
std::cout << "running_exe_Release\n";
return runRealExe(argc, argv);
}

View File

@ -0,0 +1,10 @@
#include <fstream>
#include <iostream>
#include "exe_common.h"
int main(int argc, char* argv[])
{
std::cout << "running_exe_RelWithDebInfo\n";
return runRealExe(argc, argv);
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UiA</class>
<widget class="QWidget" name="UiA">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTreeView" name="treeView"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>