Add automatic rcc invocation for Qt.

This replaces the need to invoke qt4_add_resources by allowing
adding the source .qrc file directly to the target sources.
This commit is contained in:
Stephen Kelly 2013-09-15 14:41:07 +02:00
parent 84218e1870
commit 9c87d9cc3e
17 changed files with 426 additions and 10 deletions

View File

@ -75,6 +75,8 @@ Properties on Targets
/prop_tgt/AUTOMOC
/prop_tgt/AUTOUIC
/prop_tgt/AUTOUIC_OPTIONS
/prop_tgt/AUTORCC
/prop_tgt/AUTORCC_OPTIONS
/prop_tgt/BUILD_WITH_INSTALL_RPATH
/prop_tgt/BUNDLE_EXTENSION
/prop_tgt/BUNDLE
@ -237,6 +239,7 @@ Properties on Source Files
.. toctree::
/prop_sf/ABSTRACT
/prop_sf/AUTOUIC_OPTIONS
/prop_sf/AUTORCC_OPTIONS
/prop_sf/COMPILE_DEFINITIONS_CONFIG
/prop_sf/COMPILE_DEFINITIONS
/prop_sf/COMPILE_FLAGS

View File

@ -155,6 +155,8 @@ Variables that Control the Build
/variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY
/variable/CMAKE_AUTOMOC_MOC_OPTIONS
/variable/CMAKE_AUTOMOC
/variable/CMAKE_AUTORCC
/variable/CMAKE_AUTORCC_OPTIONS
/variable/CMAKE_AUTOUIC
/variable/CMAKE_AUTOUIC_OPTIONS
/variable/CMAKE_BUILD_WITH_INSTALL_RPATH

View File

@ -0,0 +1,14 @@
AUTORCC_OPTIONS
---------------
Additional options for rcc when using autorcc (see the :prop_tgt:`AUTORCC` target
property)
This property holds additional command line options which will be used when
rcc is executed during the build via autorcc, i.e. it is equivalent to the
optional OPTIONS argument of the qt4_add_resources() macro.
By default it is empty.
The options set on the .qrc source file may override :prop_tgt:`AUTORCC_OPTIONS` set
on the target.

21
Help/prop_tgt/AUTORCC.rst Normal file
View File

@ -0,0 +1,21 @@
AUTORCC
-------
Should the target be processed with autorcc (for Qt projects).
AUTORCC is a boolean specifying whether CMake will handle
the Qt rcc code generator automatically, i.e. without having to use
the QT4_ADD_RESOURCES() or QT5_ADD_RESOURCES() macro. Currently Qt4 and Qt5 are
supported.
When this property is set to TRUE, CMake will handle .qrc files added
as target sources at build time and invoke rcc accordingly.
This property is initialized by the value of the :variable:`CMAKE_AUTORCC`
variable if it is set when a target is created.
Additional command line options for rcc can be set via the
:prop_sf:`AUTORCC_OPTIONS` source file property on the .qrc file.
The global property :prop_gbl:`AUTOGEN_TARGETS_FOLDER` can be used to group the
autouic targets together in an IDE, e.g. in MSVS.

View File

@ -0,0 +1,17 @@
AUTORCC_OPTIONS
---------------
Additional options for rcc when using autorcc (see the :prop_tgt:`AUTORCC` target property)
This property holds additional command line options
which will be used when rcc is executed during the build via autorcc,
i.e. it is equivalent to the optional OPTIONS argument of the
qt4_add_resources() macro.
By default it is empty.
This property is initialized by the value of the variable
:variable:`CMAKE_AUTORCC` if it is set when a target is created.
The options set on the target may be overridden by :prop_sf:`AUTORCC_OPTIONS` set
on the .qrc source file.

View File

@ -0,0 +1,7 @@
CMAKE_AUTORCC
-------------
Whether to handle rcc automatically for Qt targets.
This variable is used to initialize the :prop_tgt:`AUTORCC` property on all the targets.
See that target property for additional information.

View File

@ -0,0 +1,7 @@
CMAKE_AUTORCC_OPTIONS
---------------------
Whether to handle rcc automatically for Qt targets.
This variable is used to initialize the :prop_tgt:`AUTORCC_OPTIONS` property on
all the targets. See that target property for additional information.

View File

@ -1,4 +1,5 @@
set(AM_SOURCES @_moc_files@ )
set(AM_RCC_SOURCES @_rcc_files@ )
set(AM_SKIP_MOC @_skip_moc@ )
set(AM_SKIP_UIC @_skip_uic@ )
set(AM_HEADERS @_moc_headers@ )
@ -10,6 +11,7 @@ set(AM_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@/")
set(AM_CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@/")
set(AM_QT_MOC_EXECUTABLE "@_qt_moc_executable@")
set(AM_QT_UIC_EXECUTABLE "@_qt_uic_executable@")
set(AM_QT_RCC_EXECUTABLE "@_qt_rcc_executable@")
set(AM_CMAKE_CURRENT_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@/")
set(AM_CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@/")
set(AM_QT_VERSION_MAJOR "@_target_qt_version@")
@ -18,3 +20,5 @@ set(AM_RELAXED_MODE "@_moc_relaxed_mode@")
set(AM_UIC_TARGET_OPTIONS @_uic_target_options@)
set(AM_UIC_OPTIONS_FILES @_qt_uic_options_files@)
set(AM_UIC_OPTIONS_OPTIONS @_qt_uic_options_options@)
set(AM_RCC_OPTIONS_FILES @_qt_rcc_options_files@)
set(AM_RCC_OPTIONS_OPTIONS @_qt_rcc_options_options@)

View File

@ -1226,7 +1226,8 @@ void cmGlobalGenerator::CreateQtAutoGeneratorsTargets()
target.GetType() == cmTarget::OBJECT_LIBRARY)
{
if((target.GetPropertyAsBool("AUTOMOC")
|| target.GetPropertyAsBool("AUTOUIC"))
|| target.GetPropertyAsBool("AUTOUIC")
|| target.GetPropertyAsBool("AUTORCC"))
&& !target.IsImported())
{
cmQtAutoGenerators autogen;

View File

@ -119,6 +119,7 @@ cmQtAutoGenerators::cmQtAutoGenerators()
,ColorOutput(true)
,RunMocFailed(false)
,RunUicFailed(false)
,RunRccFailed(false)
,GenerateAll(false)
{
@ -266,9 +267,18 @@ void cmQtAutoGenerators::SetupAutoGenerateTarget(cmTarget* target)
{
toolNames.push_back("uic");
}
if (target->GetPropertyAsBool("AUTORCC"))
{
toolNames.push_back("rcc");
}
std::string tools = toolNames[0];
toolNames.erase(toolNames.begin());
while (toolNames.size() > 1)
{
tools += ", " + toolNames[0];
toolNames.erase(toolNames.begin());
}
if (toolNames.size() == 1)
{
tools += " and " + toolNames[0];
@ -343,6 +353,10 @@ void cmQtAutoGenerators::SetupAutoGenerateTarget(cmTarget* target)
{
this->SetupAutoUicTarget(target);
}
if (target->GetPropertyAsBool("AUTORCC"))
{
this->SetupAutoRccTarget(target);
}
const char* cmakeRoot = makefile->GetSafeDefinition("CMAKE_ROOT");
std::string inputFile = cmakeRoot;
@ -670,6 +684,168 @@ void cmQtAutoGenerators::SetupAutoUicTarget(cmTarget* target)
}
}
void cmQtAutoGenerators::MergeRccOptions(std::vector<std::string> &opts,
const std::vector<std::string> &fileOpts,
bool isQt5)
{
static const char* valueOptions[] = {
"name",
"root",
"compress",
"threshold"
};
std::vector<std::string> extraOpts;
for(std::vector<std::string>::const_iterator it = fileOpts.begin();
it != fileOpts.end(); ++it)
{
std::vector<std::string>::iterator existingIt
= std::find(opts.begin(), opts.end(), *it);
if (existingIt != opts.end())
{
const char *o = it->c_str();
if (*o == '-')
{
++o;
}
if (isQt5 && *o == '-')
{
++o;
}
if (std::find_if(cmArrayBegin(valueOptions), cmArrayEnd(valueOptions),
cmStrCmp(o)) != cmArrayEnd(valueOptions))
{
assert(existingIt + 1 != opts.end());
*(existingIt + 1) = *(it + 1);
++it;
}
}
else
{
extraOpts.push_back(*it);
}
}
opts.insert(opts.end(), extraOpts.begin(), extraOpts.end());
}
void cmQtAutoGenerators::SetupAutoRccTarget(cmTarget* target)
{
std::string _rcc_files;
const char* sepRccFiles = "";
cmMakefile *makefile = target->GetMakefile();
std::vector<cmSourceFile*> newFiles;
const std::vector<cmSourceFile*>& srcFiles = target->GetSourceFiles();
std::string rccFileFiles;
std::string rccFileOptions;
const char *sep = "";
const char *qtVersion = makefile->GetDefinition("_target_qt_version");
std::vector<std::string> rccOptions;
if (const char* opts = target->GetProperty("AUTORCC_OPTIONS"))
{
cmSystemTools::ExpandListArgument(opts, rccOptions);
}
for(std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin();
fileIt != srcFiles.end();
++fileIt)
{
cmSourceFile* sf = *fileIt;
std::string ext = sf->GetExtension();
if (ext == "qrc")
{
std::string absFile = cmsys::SystemTools::GetRealPath(
sf->GetFullPath().c_str());
bool skip = cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC"));
if (!skip)
{
_rcc_files += sepRccFiles;
_rcc_files += absFile;
sepRccFiles = ";";
std::string basename = cmsys::SystemTools::
GetFilenameWithoutLastExtension(absFile);
std::string rcc_output_file = makefile->GetCurrentOutputDirectory();
rcc_output_file += "/qrc_" + basename + ".cpp";
makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES",
rcc_output_file.c_str(), false);
cmSourceFile* rccCppSource
= makefile->GetOrCreateSource(rcc_output_file.c_str(), true);
newFiles.push_back(rccCppSource);
if (const char *prop = sf->GetProperty("AUTORCC_OPTIONS"))
{
std::vector<std::string> optsVec;
cmSystemTools::ExpandListArgument(prop, optsVec);
this->MergeRccOptions(rccOptions, optsVec,
strcmp(qtVersion, "5") == 0);
}
if (!rccOptions.empty())
{
rccFileFiles += sep;
rccFileFiles += absFile;
rccFileOptions += sep;
}
const char *listSep = "";
for(std::vector<std::string>::const_iterator it = rccOptions.begin();
it != rccOptions.end();
++it)
{
rccFileOptions += listSep;
rccFileOptions += *it;
listSep = "@list_sep@";
}
sep = ";";
}
}
}
for(std::vector<cmSourceFile*>::const_iterator fileIt = newFiles.begin();
fileIt != newFiles.end();
++fileIt)
{
target->AddSourceFile(*fileIt);
}
makefile->AddDefinition("_rcc_files",
cmLocalGenerator::EscapeForCMake(_rcc_files.c_str()).c_str());
makefile->AddDefinition("_qt_rcc_options_files",
cmLocalGenerator::EscapeForCMake(rccFileFiles.c_str()).c_str());
makefile->AddDefinition("_qt_rcc_options_options",
cmLocalGenerator::EscapeForCMake(rccFileOptions.c_str()).c_str());
const char *qtRcc = makefile->GetSafeDefinition("QT_RCC_EXECUTABLE");
makefile->AddDefinition("_qt_rcc_executable", qtRcc);
const char* targetName = target->GetName();
if (strcmp(qtVersion, "5") == 0)
{
cmTarget *qt5Rcc = makefile->FindTargetToUse("Qt5::rcc");
if (!qt5Rcc)
{
cmSystemTools::Error("Qt5::rcc target not found ",
targetName);
return;
}
makefile->AddDefinition("_qt_rcc_executable", qt5Rcc->GetLocation(0));
}
else
{
if (strcmp(qtVersion, "4") != 0)
{
cmSystemTools::Error("The CMAKE_AUTORCC feature supports only Qt 4 and "
"Qt 5 ", targetName);
}
}
}
bool cmQtAutoGenerators::Run(const char* targetDirectory, const char *config)
{
bool success = true;
@ -734,6 +910,7 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(cmMakefile* makefile,
"AM_Qt5Core_VERSION_MAJOR");
}
this->Sources = makefile->GetSafeDefinition("AM_SOURCES");
this->RccSources = makefile->GetSafeDefinition("AM_RCC_SOURCES");
this->SkipMoc = makefile->GetSafeDefinition("AM_SKIP_MOC");
this->SkipUic = makefile->GetSafeDefinition("AM_SKIP_UIC");
this->Headers = makefile->GetSafeDefinition("AM_HEADERS");
@ -743,6 +920,7 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(cmMakefile* makefile,
this->Builddir = makefile->GetSafeDefinition("AM_CMAKE_CURRENT_BINARY_DIR");
this->MocExecutable = makefile->GetSafeDefinition("AM_QT_MOC_EXECUTABLE");
this->UicExecutable = makefile->GetSafeDefinition("AM_QT_UIC_EXECUTABLE");
this->RccExecutable = makefile->GetSafeDefinition("AM_QT_RCC_EXECUTABLE");
std::string compileDefsPropOrig = "AM_MOC_COMPILE_DEFINITIONS";
std::string compileDefsProp = compileDefsPropOrig;
if(config)
@ -793,6 +971,28 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(cmMakefile* makefile,
this->UicOptions[*fileIt] = *optionIt;
}
}
{
const char *rccOptionsFiles
= makefile->GetSafeDefinition("AM_RCC_OPTIONS_FILES");
const char *rccOptionsOptions
= makefile->GetSafeDefinition("AM_RCC_OPTIONS_OPTIONS");
std::vector<std::string> rccFilesVec;
cmSystemTools::ExpandListArgument(rccOptionsFiles, rccFilesVec);
std::vector<std::string> rccOptionsVec;
cmSystemTools::ExpandListArgument(rccOptionsOptions, rccOptionsVec);
if (rccFilesVec.size() != rccOptionsVec.size())
{
return false;
}
for (std::vector<std::string>::iterator fileIt = rccFilesVec.begin(),
optionIt = rccOptionsVec.begin();
fileIt != rccFilesVec.end();
++fileIt, ++optionIt)
{
cmSystemTools::ReplaceString(*optionIt, "@list_sep@", ";");
this->RccOptions[*fileIt] = *optionIt;
}
}
this->CurrentCompileSettingsStr = this->MakeCompileSettingsString(makefile);
this->RelaxedMode = makefile->IsOn("AM_RELAXED_MODE");
@ -1044,6 +1244,11 @@ bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile)
this->GenerateUi(*it);
}
if(!this->RccExecutable.empty())
{
this->GenerateQrc();
}
cmsys_ios::stringstream outStream;
outStream << "/* This file is autogenerated, do not edit*/\n";
@ -1081,6 +1286,11 @@ bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile)
std::cerr << "uic failed..."<< std::endl;
return false;
}
if (this->RunRccFailed)
{
std::cerr << "rcc failed..."<< std::endl;
return false;
}
outStream.flush();
std::string automocSource = outStream.str();
if (!automocCppChanged)
@ -1677,6 +1887,79 @@ bool cmQtAutoGenerators::GenerateUi(const std::string& uiFileName)
return false;
}
bool cmQtAutoGenerators::GenerateQrc()
{
std::vector<std::string> sourceFiles;
cmSystemTools::ExpandListArgument(this->RccSources, sourceFiles);
for(std::vector<std::string>::const_iterator si = sourceFiles.begin();
si != sourceFiles.end(); ++si)
{
std::string ext = cmsys::SystemTools::GetFilenameLastExtension(*si);
if (ext != ".qrc")
{
continue;
}
std::vector<cmStdString> command;
command.push_back(this->RccExecutable);
std::string basename = cmsys::SystemTools::
GetFilenameWithoutLastExtension(*si);
std::string rcc_output_file = this->Builddir + "qrc_" + basename + ".cpp";
int sourceNewerThanQrc = 0;
bool success = cmsys::SystemTools::FileTimeCompare(si->c_str(),
rcc_output_file.c_str(),
&sourceNewerThanQrc);
if (this->GenerateAll || !success || sourceNewerThanQrc >= 0)
{
std::string options;
std::map<std::string, std::string>::const_iterator optionIt
= this->RccOptions.find(*si);
if (optionIt != this->RccOptions.end())
{
std::vector<std::string> opts;
cmSystemTools::ExpandListArgument(optionIt->second, opts);
for(std::vector<std::string>::const_iterator optIt = opts.begin();
optIt != opts.end();
++optIt)
{
command.push_back(*optIt);
}
}
command.push_back("-o");
command.push_back(rcc_output_file);
command.push_back(*si);
if (this->Verbose)
{
for(std::vector<cmStdString>::const_iterator cmdIt = command.begin();
cmdIt != command.end();
++cmdIt)
{
std::cout << *cmdIt << " ";
}
std::cout << std::endl;
}
std::string output;
int retVal = 0;
bool result = cmSystemTools::RunSingleCommand(command, &output, &retVal);
if (!result || retVal)
{
std::cerr << "AUTORCC: error: process for " << rcc_output_file <<
" failed:\n" << output << std::endl;
this->RunRccFailed = true;
cmSystemTools::RemoveFile(rcc_output_file.c_str());
return false;
}
}
}
return true;
}
std::string cmQtAutoGenerators::Join(const std::vector<std::string>& lst,
char separator)
{

View File

@ -32,6 +32,7 @@ private:
std::map<std::string, std::string> &configIncludes,
std::map<std::string, std::string> &configDefines);
void SetupAutoUicTarget(cmTarget* target);
void SetupAutoRccTarget(cmTarget* target);
cmGlobalGenerator* CreateGlobalGenerator(cmake* cm,
const char* targetDirectory);
@ -49,6 +50,7 @@ private:
bool GenerateMoc(const std::string& sourceFile,
const std::string& mocFileName);
bool GenerateUi(const std::string& uiFileName);
bool GenerateQrc();
void ParseCppFile(const std::string& absFilename,
const std::vector<std::string>& headerExtensions,
std::map<std::string, std::string>& includedMocs,
@ -83,8 +85,12 @@ private:
void MergeUicOptions(std::vector<std::string> &opts,
const std::vector<std::string> &fileOpts, bool isQt5);
void MergeRccOptions(std::vector<std::string> &opts,
const std::vector<std::string> &fileOpts, bool isQt5);
std::string QtMajorVersion;
std::string Sources;
std::string RccSources;
std::string SkipMoc;
std::string SkipUic;
std::string Headers;
@ -93,6 +99,7 @@ private:
std::string Builddir;
std::string MocExecutable;
std::string UicExecutable;
std::string RccExecutable;
std::string MocCompileDefinitionsStr;
std::string MocIncludesStr;
std::string MocOptionsStr;
@ -109,11 +116,13 @@ private:
std::vector<std::string> MocOptions;
std::vector<std::string> UicTargetOptions;
std::map<std::string, std::string> UicOptions;
std::map<std::string, std::string> RccOptions;
bool Verbose;
bool ColorOutput;
bool RunMocFailed;
bool RunUicFailed;
bool RunRccFailed;
bool GenerateAll;
bool RelaxedMode;

View File

@ -275,8 +275,10 @@ void cmTarget::SetMakefile(cmMakefile* mf)
this->SetPropertyDefault("OSX_ARCHITECTURES", 0);
this->SetPropertyDefault("AUTOMOC", 0);
this->SetPropertyDefault("AUTOUIC", 0);
this->SetPropertyDefault("AUTORCC", 0);
this->SetPropertyDefault("AUTOMOC_MOC_OPTIONS", 0);
this->SetPropertyDefault("AUTOUIC_OPTIONS", 0);
this->SetPropertyDefault("AUTORCC_OPTIONS", 0);
this->SetPropertyDefault("LINK_DEPENDS_NO_SHARED", 0);
this->SetPropertyDefault("LINK_INTERFACE_LIBRARIES", 0);
this->SetPropertyDefault("WIN32_EXECUTABLE", 0);

View File

@ -36,6 +36,7 @@ add_definitions(-DFOO -DSomeDefine="Barx")
set(CMAKE_AUTOMOC_RELAXED_MODE TRUE)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
# create an executable and two library targets, each requiring automoc:
add_library(codeeditorLib STATIC codeeditor.cpp)
@ -43,7 +44,9 @@ add_library(codeeditorLib STATIC codeeditor.cpp)
add_library(privateSlot OBJECT private_slot.cpp)
add_executable(QtAutogen main.cpp calwidget.cpp foo.cpp blub.cpp bar.cpp abc.cpp
xyz.cpp yaf.cpp gadget.cpp $<TARGET_OBJECTS:privateSlot>)
xyz.cpp yaf.cpp gadget.cpp $<TARGET_OBJECTS:privateSlot>
test.qrc resourcetester.cpp
)
set_target_properties(QtAutogen codeeditorLib privateSlot PROPERTIES AUTOMOC TRUE)

View File

@ -38,7 +38,8 @@
**
****************************************************************************/
#include <QApplication>
#include <QCoreApplication>
#include <QTimer>
#include "codeeditor.h"
#include "calwidget.h"
@ -49,16 +50,11 @@
#include "xyz.h"
#include "yaf.h"
#include "libC.h"
#include "resourcetester.h"
int main(int argv, char **args)
{
QApplication app(argv, args);
CodeEditor editor;
editor.setWindowTitle(QObject::tr("Code Editor Example"));
Window w;
w.setWindowTitle(QObject::tr("Window Example"));
QCoreApplication app(argv, args);
Foo foo;
foo.doFoo();
@ -81,5 +77,9 @@ int main(int argv, char **args)
LibC lc;
lc.foo();
ResourceTester rt;
QTimer::singleShot(0, &rt, SLOT(doTest()));
return app.exec();
}

View File

@ -0,0 +1,21 @@
#include "resourcetester.h"
#include <QDebug>
#include <QApplication>
#include <QFile>
#include <QTimer>
ResourceTester::ResourceTester(QObject *parent)
: QObject(parent)
{
}
void ResourceTester::doTest()
{
if (!QFile::exists(":/CMakeLists.txt"))
qApp->exit(EXIT_FAILURE);
QTimer::singleShot(0, qApp, SLOT(quit()));
}

View File

@ -0,0 +1,17 @@
#ifndef RESOURCE_TESTER_H
#define RESOURCE_TESTER_H
#include <QObject>
class ResourceTester : public QObject
{
Q_OBJECT
public:
explicit ResourceTester(QObject *parent = 0);
private slots:
void doTest();
};
#endif

5
Tests/QtAutogen/test.qrc Normal file
View File

@ -0,0 +1,5 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>CMakeLists.txt</file>
</qresource>
</RCC>