
Before skipMoc was a list of files that were not included in the sources. Now the skipMoc files are added to the sources as well in case they are needed for uic processing. skipMoc becomes a blacklist just like skipUic.
955 lines
32 KiB
C++
955 lines
32 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#include "cmQtAutoGeneratorInitializer.h"
|
|
|
|
#include "cmAlgorithms.h"
|
|
#include "cmCustomCommandLines.h"
|
|
#include "cmFilePathChecksum.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmGlobalGenerator.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmOutputConverter.h"
|
|
#include "cmSourceFile.h"
|
|
#include "cmSourceFileLocation.h"
|
|
#include "cmState.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmTarget.h"
|
|
#include "cmake.h"
|
|
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
#include "cmGlobalVisualStudioGenerator.h"
|
|
#endif
|
|
|
|
#include <algorithm>
|
|
#include <assert.h>
|
|
#include <cmConfigure.h>
|
|
#include <cmsys/FStream.hxx>
|
|
#include <cmsys/RegularExpression.hxx>
|
|
#include <map>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <string.h>
|
|
#include <string>
|
|
#include <sys/stat.h>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
static void utilCopyTargetProperty(cmTarget* destinationTarget,
|
|
cmTarget* sourceTarget,
|
|
const std::string& propertyName)
|
|
{
|
|
const char* propertyValue = sourceTarget->GetProperty(propertyName);
|
|
if (propertyValue) {
|
|
destinationTarget->SetProperty(propertyName, propertyValue);
|
|
}
|
|
}
|
|
|
|
static std::string utilStripCR(std::string const& line)
|
|
{
|
|
// Strip CR characters rcc may have printed (possibly more than one!).
|
|
std::string::size_type cr = line.find('\r');
|
|
if (cr != line.npos) {
|
|
return line.substr(0, cr);
|
|
}
|
|
return line;
|
|
}
|
|
|
|
static std::string GetAutogenTargetName(cmGeneratorTarget const* target)
|
|
{
|
|
std::string autogenTargetName = target->GetName();
|
|
autogenTargetName += "_autogen";
|
|
return autogenTargetName;
|
|
}
|
|
|
|
static std::string GetAutogenTargetFilesDir(cmGeneratorTarget const* target)
|
|
{
|
|
cmMakefile* makefile = target->Target->GetMakefile();
|
|
std::string targetDir = makefile->GetCurrentBinaryDirectory();
|
|
targetDir += makefile->GetCMakeInstance()->GetCMakeFilesDirectory();
|
|
targetDir += "/";
|
|
targetDir += GetAutogenTargetName(target);
|
|
targetDir += ".dir/";
|
|
return targetDir;
|
|
}
|
|
|
|
static std::string GetAutogenTargetBuildDir(cmGeneratorTarget const* target)
|
|
{
|
|
cmMakefile* makefile = target->Target->GetMakefile();
|
|
std::string targetDir = makefile->GetCurrentBinaryDirectory();
|
|
targetDir += "/";
|
|
targetDir += GetAutogenTargetName(target);
|
|
targetDir += "/";
|
|
return targetDir;
|
|
}
|
|
|
|
static std::string GetQtMajorVersion(cmGeneratorTarget const* target)
|
|
{
|
|
cmMakefile* makefile = target->Target->GetMakefile();
|
|
std::string qtMajorVersion = makefile->GetSafeDefinition("QT_VERSION_MAJOR");
|
|
if (qtMajorVersion.empty()) {
|
|
qtMajorVersion = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR");
|
|
}
|
|
const char* targetQtVersion =
|
|
target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", "");
|
|
if (targetQtVersion != CM_NULLPTR) {
|
|
qtMajorVersion = targetQtVersion;
|
|
}
|
|
return qtMajorVersion;
|
|
}
|
|
|
|
static void SetupSourceFiles(cmGeneratorTarget const* target,
|
|
std::vector<std::string>& skipMoc,
|
|
std::vector<std::string>& sources,
|
|
std::vector<std::string>& headers,
|
|
std::vector<std::string>& skipUic)
|
|
{
|
|
cmMakefile* makefile = target->Target->GetMakefile();
|
|
|
|
std::vector<cmSourceFile*> srcFiles;
|
|
target->GetConfigCommonSourceFiles(srcFiles);
|
|
|
|
cmFilePathChecksum fpathCheckSum(makefile);
|
|
for (std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin();
|
|
fileIt != srcFiles.end(); ++fileIt) {
|
|
cmSourceFile* sf = *fileIt;
|
|
const cmSystemTools::FileFormat fileType =
|
|
cmSystemTools::GetFileFormat(sf->GetExtension().c_str());
|
|
|
|
if (!(fileType == cmSystemTools::CXX_FILE_FORMAT) &&
|
|
!(fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
|
|
continue;
|
|
}
|
|
if (cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) {
|
|
continue;
|
|
}
|
|
const bool fileSkipUic =
|
|
cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOUIC"));
|
|
const bool fileSkipMoc =
|
|
cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOMOC"));
|
|
if (fileSkipUic && fileSkipMoc) {
|
|
continue;
|
|
}
|
|
|
|
// Use file
|
|
const std::string absFile =
|
|
cmsys::SystemTools::GetRealPath(sf->GetFullPath());
|
|
// Add file name to sources or headers list
|
|
if (fileType == cmSystemTools::CXX_FILE_FORMAT) {
|
|
sources.push_back(absFile);
|
|
} else if (fileType == cmSystemTools::HEADER_FILE_FORMAT) {
|
|
headers.push_back(absFile);
|
|
}
|
|
// Add file name to skip lists on demand
|
|
if (fileSkipUic) {
|
|
skipUic.push_back(absFile);
|
|
}
|
|
if (fileSkipMoc) {
|
|
skipMoc.push_back(absFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void GetCompileDefinitionsAndDirectories(
|
|
cmGeneratorTarget const* target, const std::string& config,
|
|
std::string& incs, std::string& defs)
|
|
{
|
|
std::vector<std::string> includeDirs;
|
|
cmLocalGenerator* localGen = target->GetLocalGenerator();
|
|
// Get the include dirs for this target, without stripping the implicit
|
|
// include dirs off, see https://gitlab.kitware.com/cmake/cmake/issues/13667
|
|
localGen->GetIncludeDirectories(includeDirs, target, "CXX", config, false);
|
|
|
|
incs = cmJoin(includeDirs, ";");
|
|
|
|
std::set<std::string> defines;
|
|
localGen->AddCompileDefinitions(defines, target, config, "CXX");
|
|
|
|
defs += cmJoin(defines, ";");
|
|
}
|
|
|
|
static void MocSetupAutoTarget(
|
|
cmGeneratorTarget const* target, const std::string& autogenTargetName,
|
|
std::vector<std::string> const& skipMoc,
|
|
std::vector<std::string> const& mocHeaders,
|
|
std::map<std::string, std::string>& configIncludes,
|
|
std::map<std::string, std::string>& configDefines)
|
|
{
|
|
cmLocalGenerator* lg = target->GetLocalGenerator();
|
|
cmMakefile* makefile = target->Target->GetMakefile();
|
|
|
|
const char* tmp = target->GetProperty("AUTOMOC_MOC_OPTIONS");
|
|
std::string _moc_options = (tmp != CM_NULLPTR ? tmp : "");
|
|
makefile->AddDefinition(
|
|
"_moc_options", cmOutputConverter::EscapeForCMake(_moc_options).c_str());
|
|
makefile->AddDefinition(
|
|
"_skip_moc",
|
|
cmOutputConverter::EscapeForCMake(cmJoin(skipMoc, ";")).c_str());
|
|
makefile->AddDefinition(
|
|
"_moc_headers",
|
|
cmOutputConverter::EscapeForCMake(cmJoin(mocHeaders, ";")).c_str());
|
|
bool relaxedMode = makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE");
|
|
makefile->AddDefinition("_moc_relaxed_mode", relaxedMode ? "TRUE" : "FALSE");
|
|
|
|
std::string _moc_incs;
|
|
std::string _moc_compile_defs;
|
|
std::vector<std::string> configs;
|
|
const std::string& config = makefile->GetConfigurations(configs);
|
|
GetCompileDefinitionsAndDirectories(target, config, _moc_incs,
|
|
_moc_compile_defs);
|
|
|
|
makefile->AddDefinition(
|
|
"_moc_incs", cmOutputConverter::EscapeForCMake(_moc_incs).c_str());
|
|
makefile->AddDefinition(
|
|
"_moc_compile_defs",
|
|
cmOutputConverter::EscapeForCMake(_moc_compile_defs).c_str());
|
|
|
|
for (std::vector<std::string>::const_iterator li = configs.begin();
|
|
li != configs.end(); ++li) {
|
|
std::string config_moc_incs;
|
|
std::string config_moc_compile_defs;
|
|
GetCompileDefinitionsAndDirectories(target, *li, config_moc_incs,
|
|
config_moc_compile_defs);
|
|
if (config_moc_incs != _moc_incs) {
|
|
configIncludes[*li] = cmOutputConverter::EscapeForCMake(config_moc_incs);
|
|
if (_moc_incs.empty()) {
|
|
_moc_incs = config_moc_incs;
|
|
}
|
|
}
|
|
if (config_moc_compile_defs != _moc_compile_defs) {
|
|
configDefines[*li] =
|
|
cmOutputConverter::EscapeForCMake(config_moc_compile_defs);
|
|
if (_moc_compile_defs.empty()) {
|
|
_moc_compile_defs = config_moc_compile_defs;
|
|
}
|
|
}
|
|
}
|
|
|
|
const char* qtVersion = makefile->GetDefinition("_target_qt_version");
|
|
if (strcmp(qtVersion, "5") == 0) {
|
|
cmGeneratorTarget* qt5Moc = lg->FindGeneratorTargetToUse("Qt5::moc");
|
|
if (!qt5Moc) {
|
|
cmSystemTools::Error("Qt5::moc target not found ",
|
|
autogenTargetName.c_str());
|
|
return;
|
|
}
|
|
makefile->AddDefinition("_qt_moc_executable",
|
|
qt5Moc->ImportedGetLocation(""));
|
|
} else if (strcmp(qtVersion, "4") == 0) {
|
|
cmGeneratorTarget* qt4Moc = lg->FindGeneratorTargetToUse("Qt4::moc");
|
|
if (!qt4Moc) {
|
|
cmSystemTools::Error("Qt4::moc target not found ",
|
|
autogenTargetName.c_str());
|
|
return;
|
|
}
|
|
makefile->AddDefinition("_qt_moc_executable",
|
|
qt4Moc->ImportedGetLocation(""));
|
|
} else {
|
|
cmSystemTools::Error("The CMAKE_AUTOMOC feature supports only Qt 4 and "
|
|
"Qt 5 ",
|
|
autogenTargetName.c_str());
|
|
}
|
|
}
|
|
|
|
static void UicGetOpts(cmGeneratorTarget const* target,
|
|
const std::string& config, std::string& optString)
|
|
{
|
|
std::vector<std::string> opts;
|
|
target->GetAutoUicOptions(opts, config);
|
|
optString = cmJoin(opts, ";");
|
|
}
|
|
|
|
static void UicSetupAutoTarget(
|
|
cmGeneratorTarget const* target, std::vector<std::string> const& skipUic,
|
|
std::map<std::string, std::string>& configUicOptions)
|
|
{
|
|
cmLocalGenerator* lg = target->GetLocalGenerator();
|
|
cmMakefile* makefile = target->Target->GetMakefile();
|
|
|
|
std::set<std::string> skipped;
|
|
skipped.insert(skipUic.begin(), skipUic.end());
|
|
|
|
makefile->AddDefinition(
|
|
"_skip_uic",
|
|
cmOutputConverter::EscapeForCMake(cmJoin(skipUic, ";")).c_str());
|
|
|
|
std::vector<cmSourceFile*> uiFilesWithOptions =
|
|
makefile->GetQtUiFilesWithOptions();
|
|
|
|
const char* qtVersion = makefile->GetDefinition("_target_qt_version");
|
|
|
|
std::string _uic_opts;
|
|
std::vector<std::string> configs;
|
|
const std::string& config = makefile->GetConfigurations(configs);
|
|
UicGetOpts(target, config, _uic_opts);
|
|
|
|
if (!_uic_opts.empty()) {
|
|
_uic_opts = cmOutputConverter::EscapeForCMake(_uic_opts);
|
|
makefile->AddDefinition("_uic_target_options", _uic_opts.c_str());
|
|
}
|
|
for (std::vector<std::string>::const_iterator li = configs.begin();
|
|
li != configs.end(); ++li) {
|
|
std::string config_uic_opts;
|
|
UicGetOpts(target, *li, config_uic_opts);
|
|
if (config_uic_opts != _uic_opts) {
|
|
configUicOptions[*li] =
|
|
cmOutputConverter::EscapeForCMake(config_uic_opts);
|
|
if (_uic_opts.empty()) {
|
|
_uic_opts = config_uic_opts;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string uiFileFiles;
|
|
std::string uiFileOptions;
|
|
const char* sep = "";
|
|
|
|
for (std::vector<cmSourceFile*>::const_iterator fileIt =
|
|
uiFilesWithOptions.begin();
|
|
fileIt != uiFilesWithOptions.end(); ++fileIt) {
|
|
cmSourceFile* sf = *fileIt;
|
|
std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath());
|
|
|
|
if (!skipped.insert(absFile).second) {
|
|
continue;
|
|
}
|
|
uiFileFiles += sep;
|
|
uiFileFiles += absFile;
|
|
uiFileOptions += sep;
|
|
std::string opts = sf->GetProperty("AUTOUIC_OPTIONS");
|
|
cmSystemTools::ReplaceString(opts, ";", "@list_sep@");
|
|
uiFileOptions += opts;
|
|
sep = ";";
|
|
}
|
|
|
|
makefile->AddDefinition(
|
|
"_qt_uic_options_files",
|
|
cmOutputConverter::EscapeForCMake(uiFileFiles).c_str());
|
|
makefile->AddDefinition(
|
|
"_qt_uic_options_options",
|
|
cmOutputConverter::EscapeForCMake(uiFileOptions).c_str());
|
|
|
|
std::string targetName = target->GetName();
|
|
if (strcmp(qtVersion, "5") == 0) {
|
|
cmGeneratorTarget* qt5Uic = lg->FindGeneratorTargetToUse("Qt5::uic");
|
|
if (!qt5Uic) {
|
|
// Project does not use Qt5Widgets, but has AUTOUIC ON anyway
|
|
} else {
|
|
makefile->AddDefinition("_qt_uic_executable",
|
|
qt5Uic->ImportedGetLocation(""));
|
|
}
|
|
} else if (strcmp(qtVersion, "4") == 0) {
|
|
cmGeneratorTarget* qt4Uic = lg->FindGeneratorTargetToUse("Qt4::uic");
|
|
if (!qt4Uic) {
|
|
cmSystemTools::Error("Qt4::uic target not found ", targetName.c_str());
|
|
return;
|
|
}
|
|
makefile->AddDefinition("_qt_uic_executable",
|
|
qt4Uic->ImportedGetLocation(""));
|
|
} else {
|
|
cmSystemTools::Error("The CMAKE_AUTOUIC feature supports only Qt 4 and "
|
|
"Qt 5 ",
|
|
targetName.c_str());
|
|
}
|
|
}
|
|
|
|
static std::string RccGetExecutable(cmGeneratorTarget const* target,
|
|
const std::string& qtMajorVersion)
|
|
{
|
|
cmLocalGenerator* lg = target->GetLocalGenerator();
|
|
|
|
std::string const& targetName = target->GetName();
|
|
if (qtMajorVersion == "5") {
|
|
cmGeneratorTarget* qt5Rcc = lg->FindGeneratorTargetToUse("Qt5::rcc");
|
|
if (!qt5Rcc) {
|
|
cmSystemTools::Error("Qt5::rcc target not found ", targetName.c_str());
|
|
return std::string();
|
|
}
|
|
return qt5Rcc->ImportedGetLocation("");
|
|
}
|
|
if (qtMajorVersion == "4") {
|
|
cmGeneratorTarget* qt4Rcc = lg->FindGeneratorTargetToUse("Qt4::rcc");
|
|
if (!qt4Rcc) {
|
|
cmSystemTools::Error("Qt4::rcc target not found ", targetName.c_str());
|
|
return std::string();
|
|
}
|
|
return qt4Rcc->ImportedGetLocation("");
|
|
}
|
|
|
|
cmSystemTools::Error("The CMAKE_AUTORCC feature supports only Qt 4 and "
|
|
"Qt 5 ",
|
|
targetName.c_str());
|
|
return std::string();
|
|
}
|
|
|
|
static void RccMergeOptions(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(*it)) != 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());
|
|
}
|
|
|
|
/// @brief Reads the resource files list from from a .qrc file - Qt5 version
|
|
/// @return True if the .qrc file was successfully parsed
|
|
static bool RccListInputsQt5(cmSourceFile* sf, cmGeneratorTarget const* target,
|
|
std::vector<std::string>& depends)
|
|
{
|
|
const std::string rccCommand = RccGetExecutable(target, "5");
|
|
if (rccCommand.empty()) {
|
|
cmSystemTools::Error("AUTOGEN: error: rcc executable not available\n");
|
|
return false;
|
|
}
|
|
|
|
bool hasDashDashList = false;
|
|
// Read rcc features
|
|
{
|
|
std::vector<std::string> command;
|
|
command.push_back(rccCommand);
|
|
command.push_back("--help");
|
|
std::string rccStdOut;
|
|
std::string rccStdErr;
|
|
int retVal = 0;
|
|
bool result =
|
|
cmSystemTools::RunSingleCommand(command, &rccStdOut, &rccStdErr, &retVal,
|
|
CM_NULLPTR, cmSystemTools::OUTPUT_NONE);
|
|
if (result && retVal == 0 &&
|
|
rccStdOut.find("--list") != std::string::npos) {
|
|
hasDashDashList = true;
|
|
}
|
|
}
|
|
// Run rcc list command
|
|
std::vector<std::string> command;
|
|
command.push_back(rccCommand);
|
|
command.push_back(hasDashDashList ? "--list" : "-list");
|
|
|
|
std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath());
|
|
command.push_back(absFile);
|
|
|
|
std::string rccStdOut;
|
|
std::string rccStdErr;
|
|
int retVal = 0;
|
|
bool result =
|
|
cmSystemTools::RunSingleCommand(command, &rccStdOut, &rccStdErr, &retVal,
|
|
CM_NULLPTR, cmSystemTools::OUTPUT_NONE);
|
|
if (!result || retVal) {
|
|
std::ostringstream err;
|
|
err << "AUTOGEN: error: Rcc list process for " << sf->GetFullPath()
|
|
<< " failed:\n"
|
|
<< rccStdOut << "\n"
|
|
<< rccStdErr << std::endl;
|
|
cmSystemTools::Error(err.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
// Parse rcc list output
|
|
{
|
|
std::istringstream ostr(rccStdOut);
|
|
std::string oline;
|
|
while (std::getline(ostr, oline)) {
|
|
oline = utilStripCR(oline);
|
|
if (!oline.empty()) {
|
|
depends.push_back(oline);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
std::istringstream estr(rccStdErr);
|
|
std::string eline;
|
|
while (std::getline(estr, eline)) {
|
|
eline = utilStripCR(eline);
|
|
if (cmHasLiteralPrefix(eline, "RCC: Error in")) {
|
|
static std::string searchString = "Cannot find file '";
|
|
|
|
std::string::size_type pos = eline.find(searchString);
|
|
if (pos == std::string::npos) {
|
|
std::ostringstream err;
|
|
err << "AUTOGEN: error: Rcc lists unparsable output " << eline
|
|
<< std::endl;
|
|
cmSystemTools::Error(err.str().c_str());
|
|
return false;
|
|
}
|
|
pos += searchString.length();
|
|
std::string::size_type sz = eline.size() - pos - 1;
|
|
depends.push_back(eline.substr(pos, sz));
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// @brief Reads the resource files list from from a .qrc file - Qt4 version
|
|
/// @return True if the .qrc file was successfully parsed
|
|
static bool RccListInputsQt4(cmSourceFile* sf,
|
|
std::vector<std::string>& depends)
|
|
{
|
|
// Read file into string
|
|
std::string qrcContents;
|
|
{
|
|
std::ostringstream stream;
|
|
stream << cmsys::ifstream(sf->GetFullPath().c_str()).rdbuf();
|
|
qrcContents = stream.str();
|
|
}
|
|
|
|
cmsys::RegularExpression fileMatchRegex("(<file[^<]+)");
|
|
|
|
size_t offset = 0;
|
|
while (fileMatchRegex.find(qrcContents.c_str() + offset)) {
|
|
std::string qrcEntry = fileMatchRegex.match(1);
|
|
|
|
offset += qrcEntry.size();
|
|
|
|
cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)");
|
|
fileReplaceRegex.find(qrcEntry);
|
|
std::string tag = fileReplaceRegex.match(1);
|
|
|
|
qrcEntry = qrcEntry.substr(tag.size());
|
|
|
|
if (!cmSystemTools::FileIsFullPath(qrcEntry.c_str())) {
|
|
qrcEntry = sf->GetLocation().GetDirectory() + "/" + qrcEntry;
|
|
}
|
|
|
|
depends.push_back(qrcEntry);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// @brief Reads the resource files list from from a .qrc file
|
|
/// @return True if the rcc file was successfully parsed
|
|
static bool RccListInputs(const std::string& qtMajorVersion, cmSourceFile* sf,
|
|
cmGeneratorTarget const* target,
|
|
std::vector<std::string>& depends)
|
|
{
|
|
if (qtMajorVersion == "5") {
|
|
return RccListInputsQt5(sf, target, depends);
|
|
}
|
|
return RccListInputsQt4(sf, depends);
|
|
}
|
|
|
|
static void RccSetupAutoTarget(cmGeneratorTarget const* target,
|
|
const std::string& qtMajorVersion)
|
|
{
|
|
std::string _rcc_files;
|
|
const char* sepRccFiles = "";
|
|
cmMakefile* makefile = target->Target->GetMakefile();
|
|
|
|
std::vector<cmSourceFile*> srcFiles;
|
|
target->GetConfigCommonSourceFiles(srcFiles);
|
|
|
|
std::string qrcInputs;
|
|
const char* qrcInputsSep = "";
|
|
|
|
std::string rccFileFiles;
|
|
std::string rccFileOptions;
|
|
const char* optionSep = "";
|
|
|
|
const bool qtMajorVersion5 = (qtMajorVersion == "5");
|
|
|
|
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());
|
|
bool skip = cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC"));
|
|
|
|
if (!skip) {
|
|
_rcc_files += sepRccFiles;
|
|
_rcc_files += absFile;
|
|
sepRccFiles = ";";
|
|
|
|
if (const char* prop = sf->GetProperty("AUTORCC_OPTIONS")) {
|
|
std::vector<std::string> optsVec;
|
|
cmSystemTools::ExpandListArgument(prop, optsVec);
|
|
RccMergeOptions(rccOptions, optsVec, qtMajorVersion5);
|
|
}
|
|
|
|
if (!rccOptions.empty()) {
|
|
rccFileFiles += optionSep;
|
|
rccFileFiles += absFile;
|
|
rccFileOptions += optionSep;
|
|
}
|
|
const char* listSep = "";
|
|
for (std::vector<std::string>::const_iterator it = rccOptions.begin();
|
|
it != rccOptions.end(); ++it) {
|
|
rccFileOptions += listSep;
|
|
rccFileOptions += *it;
|
|
listSep = "@list_sep@";
|
|
}
|
|
optionSep = ";";
|
|
|
|
std::string entriesList;
|
|
if (!cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) {
|
|
std::vector<std::string> depends;
|
|
if (RccListInputs(qtMajorVersion, sf, target, depends)) {
|
|
entriesList = cmJoin(depends, "@list_sep@");
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
qrcInputs += qrcInputsSep;
|
|
qrcInputs += entriesList;
|
|
qrcInputsSep = ";";
|
|
}
|
|
}
|
|
}
|
|
makefile->AddDefinition(
|
|
"_rcc_inputs", cmOutputConverter::EscapeForCMake(qrcInputs).c_str());
|
|
makefile->AddDefinition(
|
|
"_rcc_files", cmOutputConverter::EscapeForCMake(_rcc_files).c_str());
|
|
makefile->AddDefinition(
|
|
"_rcc_options_files",
|
|
cmOutputConverter::EscapeForCMake(rccFileFiles).c_str());
|
|
makefile->AddDefinition(
|
|
"_rcc_options_options",
|
|
cmOutputConverter::EscapeForCMake(rccFileOptions).c_str());
|
|
makefile->AddDefinition("_qt_rcc_executable",
|
|
RccGetExecutable(target, qtMajorVersion).c_str());
|
|
}
|
|
|
|
void cmQtAutoGeneratorInitializer::InitializeAutogenSources(
|
|
cmGeneratorTarget* target)
|
|
{
|
|
if (target->GetPropertyAsBool("AUTOMOC")) {
|
|
cmMakefile* makefile = target->Target->GetMakefile();
|
|
const std::string mocCppFile =
|
|
GetAutogenTargetBuildDir(target) + "moc_compilation.cpp";
|
|
makefile->GetOrCreateSource(mocCppFile, true);
|
|
target->AddSource(mocCppFile);
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorInitializer::InitializeAutogenTarget(
|
|
cmLocalGenerator* lg, cmGeneratorTarget* target)
|
|
{
|
|
cmMakefile* makefile = target->Target->GetMakefile();
|
|
|
|
// Create a custom target for running generators at buildtime
|
|
const std::string autogenTargetName = GetAutogenTargetName(target);
|
|
const std::string autogenBuildDir = GetAutogenTargetBuildDir(target);
|
|
const std::string workingDirectory =
|
|
cmSystemTools::CollapseFullPath("", makefile->GetCurrentBinaryDirectory());
|
|
const std::string qtMajorVersion = GetQtMajorVersion(target);
|
|
std::vector<std::string> autogenOutputFiles;
|
|
|
|
// Create autogen target build directory and add it to the clean files
|
|
cmSystemTools::MakeDirectory(autogenBuildDir);
|
|
makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES",
|
|
autogenBuildDir.c_str(), false);
|
|
|
|
if (target->GetPropertyAsBool("AUTOMOC") ||
|
|
target->GetPropertyAsBool("AUTOUIC")) {
|
|
// Create autogen target includes directory and
|
|
// add it to the origin target INCLUDE_DIRECTORIES
|
|
const std::string incsDir = autogenBuildDir + "include";
|
|
cmSystemTools::MakeDirectory(incsDir);
|
|
target->AddIncludeDirectory(incsDir, true);
|
|
}
|
|
|
|
if (target->GetPropertyAsBool("AUTOMOC")) {
|
|
// Register moc compilation file as generated
|
|
autogenOutputFiles.push_back(autogenBuildDir + "moc_compilation.cpp");
|
|
}
|
|
|
|
// Initialize autogen target dependencies
|
|
std::vector<std::string> depends;
|
|
if (const char* autogenDepends =
|
|
target->GetProperty("AUTOGEN_TARGET_DEPENDS")) {
|
|
cmSystemTools::ExpandListArgument(autogenDepends, depends);
|
|
}
|
|
|
|
// Compose command lines
|
|
cmCustomCommandLines commandLines;
|
|
{
|
|
cmCustomCommandLine currentLine;
|
|
currentLine.push_back(cmSystemTools::GetCMakeCommand());
|
|
currentLine.push_back("-E");
|
|
currentLine.push_back("cmake_autogen");
|
|
currentLine.push_back(GetAutogenTargetFilesDir(target));
|
|
currentLine.push_back("$<CONFIGURATION>");
|
|
commandLines.push_back(currentLine);
|
|
}
|
|
|
|
// Compose target comment
|
|
std::string autogenComment;
|
|
{
|
|
std::vector<std::string> toolNames;
|
|
if (target->GetPropertyAsBool("AUTOMOC")) {
|
|
toolNames.push_back("MOC");
|
|
}
|
|
if (target->GetPropertyAsBool("AUTOUIC")) {
|
|
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];
|
|
}
|
|
autogenComment = "Automatic " + tools + " for target " + target->GetName();
|
|
}
|
|
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
bool usePRE_BUILD = false;
|
|
cmGlobalGenerator* gg = lg->GetGlobalGenerator();
|
|
if (gg->GetName().find("Visual Studio") != std::string::npos) {
|
|
cmGlobalVisualStudioGenerator* vsgg =
|
|
static_cast<cmGlobalVisualStudioGenerator*>(gg);
|
|
// Under VS >= 7 use a PRE_BUILD event instead of a separate target to
|
|
// reduce the number of targets loaded into the IDE.
|
|
// This also works around a VS 11 bug that may skip updating the target:
|
|
// https://connect.microsoft.com/VisualStudio/feedback/details/769495
|
|
usePRE_BUILD = vsgg->GetVersion() >= cmGlobalVisualStudioGenerator::VS7;
|
|
if (usePRE_BUILD) {
|
|
// If the autogen target depends on an other target
|
|
// don't use PRE_BUILD
|
|
for (std::vector<std::string>::iterator it = depends.begin();
|
|
it != depends.end(); ++it) {
|
|
if (!makefile->FindTargetToUse(it->c_str())) {
|
|
usePRE_BUILD = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (target->GetPropertyAsBool("AUTORCC")) {
|
|
cmFilePathChecksum fpathCheckSum(makefile);
|
|
std::vector<cmSourceFile*> srcFiles;
|
|
target->GetConfigCommonSourceFiles(srcFiles);
|
|
for (std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin();
|
|
fileIt != srcFiles.end(); ++fileIt) {
|
|
cmSourceFile* sf = *fileIt;
|
|
if (sf->GetExtension() == "qrc" &&
|
|
!cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC"))) {
|
|
{
|
|
const std::string absFile =
|
|
cmsys::SystemTools::GetRealPath(sf->GetFullPath());
|
|
|
|
// Run cmake again when .qrc file changes
|
|
makefile->AddCMakeDependFile(absFile);
|
|
|
|
std::string rccOutputFile = autogenBuildDir;
|
|
rccOutputFile += fpathCheckSum.getPart(absFile);
|
|
rccOutputFile += "/qrc_";
|
|
rccOutputFile +=
|
|
cmsys::SystemTools::GetFilenameWithoutLastExtension(absFile);
|
|
rccOutputFile += ".cpp";
|
|
|
|
// Add rcc output file to origin target sources
|
|
makefile->GetOrCreateSource(rccOutputFile, true);
|
|
target->AddSource(rccOutputFile);
|
|
// Register rcc output file as generated
|
|
autogenOutputFiles.push_back(rccOutputFile);
|
|
}
|
|
if (lg->GetGlobalGenerator()->GetName() == "Ninja"
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
|| usePRE_BUILD
|
|
#endif
|
|
) {
|
|
if (!cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) {
|
|
RccListInputs(qtMajorVersion, sf, target, depends);
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
// Cannot use PRE_BUILD because the resource files themselves
|
|
// may not be sources within the target so VS may not know the
|
|
// target needs to re-build at all.
|
|
usePRE_BUILD = false;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
if (usePRE_BUILD) {
|
|
// Add the pre-build command directly to bypass the OBJECT_LIBRARY
|
|
// rejection in cmMakefile::AddCustomCommandToTarget because we know
|
|
// PRE_BUILD will work for an OBJECT_LIBRARY in this specific case.
|
|
std::vector<std::string> no_output;
|
|
std::vector<std::string> no_byproducts;
|
|
cmCustomCommand cc(makefile, no_output, no_byproducts, depends,
|
|
commandLines, autogenComment.c_str(),
|
|
workingDirectory.c_str());
|
|
cc.SetEscapeOldStyle(false);
|
|
cc.SetEscapeAllowMakeVars(true);
|
|
target->Target->AddPreBuildCommand(cc);
|
|
} else
|
|
#endif
|
|
{
|
|
cmTarget* autogenTarget = makefile->AddUtilityCommand(
|
|
autogenTargetName, true, workingDirectory.c_str(),
|
|
/*byproducts=*/autogenOutputFiles, depends, commandLines, false,
|
|
autogenComment.c_str());
|
|
|
|
cmGeneratorTarget* gt = new cmGeneratorTarget(autogenTarget, lg);
|
|
lg->AddGeneratorTarget(gt);
|
|
|
|
// Set target folder
|
|
const char* autogenFolder =
|
|
makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
|
|
if (!autogenFolder) {
|
|
autogenFolder =
|
|
makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER");
|
|
}
|
|
if (autogenFolder && *autogenFolder) {
|
|
autogenTarget->SetProperty("FOLDER", autogenFolder);
|
|
} else {
|
|
// inherit FOLDER property from target (#13688)
|
|
utilCopyTargetProperty(gt->Target, target->Target, "FOLDER");
|
|
}
|
|
|
|
target->Target->AddUtility(autogenTargetName);
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget(
|
|
cmGeneratorTarget const* target)
|
|
{
|
|
cmMakefile* makefile = target->Target->GetMakefile();
|
|
|
|
// forget the variables added here afterwards again:
|
|
cmMakefile::ScopePushPop varScope(makefile);
|
|
static_cast<void>(varScope);
|
|
|
|
// create a custom target for running generators at buildtime:
|
|
const std::string autogenTargetName = GetAutogenTargetName(target);
|
|
const std::string qtMajorVersion = GetQtMajorVersion(target);
|
|
|
|
makefile->AddDefinition(
|
|
"_moc_target_name",
|
|
cmOutputConverter::EscapeForCMake(autogenTargetName).c_str());
|
|
makefile->AddDefinition(
|
|
"_origin_target_name",
|
|
cmOutputConverter::EscapeForCMake(target->GetName()).c_str());
|
|
makefile->AddDefinition("_target_qt_version", qtMajorVersion.c_str());
|
|
|
|
std::vector<std::string> skipUic;
|
|
std::vector<std::string> skipMoc;
|
|
std::vector<std::string> mocSources;
|
|
std::vector<std::string> mocHeaders;
|
|
std::map<std::string, std::string> configMocIncludes;
|
|
std::map<std::string, std::string> configMocDefines;
|
|
std::map<std::string, std::string> configUicOptions;
|
|
|
|
if (target->GetPropertyAsBool("AUTOMOC") ||
|
|
target->GetPropertyAsBool("AUTOUIC") ||
|
|
target->GetPropertyAsBool("AUTORCC")) {
|
|
SetupSourceFiles(target, skipMoc, mocSources, mocHeaders, skipUic);
|
|
}
|
|
makefile->AddDefinition(
|
|
"_cpp_files",
|
|
cmOutputConverter::EscapeForCMake(cmJoin(mocSources, ";")).c_str());
|
|
if (target->GetPropertyAsBool("AUTOMOC")) {
|
|
MocSetupAutoTarget(target, autogenTargetName, skipMoc, mocHeaders,
|
|
configMocIncludes, configMocDefines);
|
|
}
|
|
if (target->GetPropertyAsBool("AUTOUIC")) {
|
|
UicSetupAutoTarget(target, skipUic, configUicOptions);
|
|
}
|
|
if (target->GetPropertyAsBool("AUTORCC")) {
|
|
RccSetupAutoTarget(target, qtMajorVersion);
|
|
}
|
|
|
|
// Generate config file
|
|
std::string inputFile = cmSystemTools::GetCMakeRoot();
|
|
inputFile += "/Modules/AutogenInfo.cmake.in";
|
|
std::string outputFile = GetAutogenTargetFilesDir(target);
|
|
outputFile += "/AutogenInfo.cmake";
|
|
|
|
makefile->ConfigureFile(inputFile.c_str(), outputFile.c_str(), false, true,
|
|
false);
|
|
|
|
// Append custom definitions to config file
|
|
if (!configMocDefines.empty() || !configMocIncludes.empty() ||
|
|
!configUicOptions.empty()) {
|
|
|
|
// Ensure we have write permission in case .in was read-only.
|
|
mode_t perm = 0;
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
mode_t mode_write = S_IWRITE;
|
|
#else
|
|
mode_t mode_write = S_IWUSR;
|
|
#endif
|
|
cmSystemTools::GetPermissions(outputFile, perm);
|
|
if (!(perm & mode_write)) {
|
|
cmSystemTools::SetPermissions(outputFile, perm | mode_write);
|
|
}
|
|
|
|
cmsys::ofstream infoFile(outputFile.c_str(), std::ios::app);
|
|
if (!infoFile) {
|
|
std::string error = "Internal CMake error when trying to open file: ";
|
|
error += outputFile;
|
|
error += " for writing.";
|
|
cmSystemTools::Error(error.c_str());
|
|
return;
|
|
}
|
|
if (!configMocDefines.empty()) {
|
|
for (std::map<std::string, std::string>::iterator
|
|
it = configMocDefines.begin(),
|
|
end = configMocDefines.end();
|
|
it != end; ++it) {
|
|
infoFile << "set(AM_MOC_COMPILE_DEFINITIONS_" << it->first << " "
|
|
<< it->second << ")\n";
|
|
}
|
|
}
|
|
if (!configMocIncludes.empty()) {
|
|
for (std::map<std::string, std::string>::iterator
|
|
it = configMocIncludes.begin(),
|
|
end = configMocIncludes.end();
|
|
it != end; ++it) {
|
|
infoFile << "set(AM_MOC_INCLUDES_" << it->first << " " << it->second
|
|
<< ")\n";
|
|
}
|
|
}
|
|
if (!configUicOptions.empty()) {
|
|
for (std::map<std::string, std::string>::iterator
|
|
it = configUicOptions.begin(),
|
|
end = configUicOptions.end();
|
|
it != end; ++it) {
|
|
infoFile << "set(AM_UIC_TARGET_OPTIONS_" << it->first << " "
|
|
<< it->second << ")\n";
|
|
}
|
|
}
|
|
}
|
|
}
|