| 1 | /**************************************************************************** | 
|---|
| 2 | ** | 
|---|
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. | 
|---|
| 4 | ** Contact: https://www.qt.io/licensing/ | 
|---|
| 5 | ** | 
|---|
| 6 | ** This file is part of the qmake application of the Qt Toolkit. | 
|---|
| 7 | ** | 
|---|
| 8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ | 
|---|
| 9 | ** Commercial License Usage | 
|---|
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
|---|
| 11 | ** accordance with the commercial license agreement provided with the | 
|---|
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
|---|
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
|---|
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
|---|
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
|---|
| 16 | ** | 
|---|
| 17 | ** GNU General Public License Usage | 
|---|
| 18 | ** Alternatively, this file may be used under the terms of the GNU | 
|---|
| 19 | ** General Public License version 3 as published by the Free Software | 
|---|
| 20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT | 
|---|
| 21 | ** included in the packaging of this file. Please review the following | 
|---|
| 22 | ** information to ensure the GNU General Public License requirements will | 
|---|
| 23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | 
|---|
| 24 | ** | 
|---|
| 25 | ** $QT_END_LICENSE$ | 
|---|
| 26 | ** | 
|---|
| 27 | ****************************************************************************/ | 
|---|
| 28 |  | 
|---|
| 29 | #include "msvc_vcproj.h" | 
|---|
| 30 | #include "option.h" | 
|---|
| 31 | #include "xmloutput.h" | 
|---|
| 32 |  | 
|---|
| 33 | #include <ioutils.h> | 
|---|
| 34 |  | 
|---|
| 35 | #include <qdir.h> | 
|---|
| 36 | #include <qdiriterator.h> | 
|---|
| 37 | #include <qcryptographichash.h> | 
|---|
| 38 | #include <qhash.h> | 
|---|
| 39 | #include <quuid.h> | 
|---|
| 40 |  | 
|---|
| 41 | #include <stdlib.h> | 
|---|
| 42 |  | 
|---|
| 43 | //#define DEBUG_SOLUTION_GEN | 
|---|
| 44 |  | 
|---|
| 45 | using namespace QMakeInternal; | 
|---|
| 46 |  | 
|---|
| 47 | QT_BEGIN_NAMESPACE | 
|---|
| 48 | // Filter GUIDs (Do NOT change these!) ------------------------------ | 
|---|
| 49 | const char _GUIDSourceFiles[]          = "{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"; | 
|---|
| 50 | const char []          = "{93995380-89BD-4b04-88EB-625FBE52EBFB}"; | 
|---|
| 51 | const char _GUIDGeneratedFiles[]       = "{71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}"; | 
|---|
| 52 | const char _GUIDResourceFiles[]        = "{D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}"; | 
|---|
| 53 | const char _GUIDLexYaccFiles[]         = "{E12AE0D2-192F-4d59-BD23-7D3FA58D3183}"; | 
|---|
| 54 | const char _GUIDTranslationFiles[]     = "{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}"; | 
|---|
| 55 | const char _GUIDFormFiles[]            = "{99349809-55BA-4b9d-BF79-8FDBB0286EB3}"; | 
|---|
| 56 | const char []   = "{E0D8C965-CC5F-43d7-AD63-FAEF0BBC0F85}"; | 
|---|
| 57 | const char _GUIDDistributionFiles[]    = "{B83CAF91-C7BF-462F-B76C-EA11631F866C}"; | 
|---|
| 58 |  | 
|---|
| 59 | // Flatfile Tags ---------------------------------------------------- | 
|---|
| 60 | const char []       = "Microsoft Visual Studio Solution File, Format Version 7.00"; | 
|---|
| 61 | const char []       = "Microsoft Visual Studio Solution File, Format Version 8.00"; | 
|---|
| 62 | const char []       = "Microsoft Visual Studio Solution File, Format Version 9.00" | 
|---|
| 63 | "\n# Visual Studio 2005"; | 
|---|
| 64 | const char []       = "Microsoft Visual Studio Solution File, Format Version 10.00" | 
|---|
| 65 | "\n# Visual Studio 2008"; | 
|---|
| 66 | const char []      = "Microsoft Visual Studio Solution File, Format Version 11.00" | 
|---|
| 67 | "\n# Visual Studio 2010"; | 
|---|
| 68 | const char []      = "Microsoft Visual Studio Solution File, Format Version 12.00" | 
|---|
| 69 | "\n# Visual Studio 2012"; | 
|---|
| 70 | const char []      = "Microsoft Visual Studio Solution File, Format Version 12.00" | 
|---|
| 71 | "\n# Visual Studio 2013"; | 
|---|
| 72 | const char []      = "Microsoft Visual Studio Solution File, Format Version 12.00" | 
|---|
| 73 | "\n# Visual Studio 2015"; | 
|---|
| 74 | const char []      = "Microsoft Visual Studio Solution File, Format Version 12.00" | 
|---|
| 75 | "\n# Visual Studio 15"; | 
|---|
| 76 | const char []      = "Microsoft Visual Studio Solution File, Format Version 12.00" | 
|---|
| 77 | "\n# Visual Studio Version 16"; | 
|---|
| 78 | // The following UUID _may_ change for later servicepacks... | 
|---|
| 79 | // If so we need to search through the registry at | 
|---|
| 80 | // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.0\Projects | 
|---|
| 81 | // to find the subkey that contains a "PossibleProjectExtension" | 
|---|
| 82 | // containing "vcproj"... | 
|---|
| 83 | // Use the hardcoded value for now so projects generated on other | 
|---|
| 84 | // platforms are actually usable. | 
|---|
| 85 | const char _slnMSVCvcprojGUID[] = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"; | 
|---|
| 86 | const char _slnProjectBeg[]     = "\nProject(\""; | 
|---|
| 87 | const char _slnProjectMid[]     = "\") = "; | 
|---|
| 88 | const char _slnProjectEnd[]     = "\nEndProject"; | 
|---|
| 89 | const char _slnGlobalBeg[]      = "\nGlobal"; | 
|---|
| 90 | const char _slnGlobalEnd[]      = "\nEndGlobal"; | 
|---|
| 91 | const char _slnSolutionConf[]   = "\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution" | 
|---|
| 92 | "\n\t\tDebug|Win32 = Debug|Win32" | 
|---|
| 93 | "\n\t\tRelease|Win32 = Release|Win32" | 
|---|
| 94 | "\n\tEndGlobalSection"; | 
|---|
| 95 |  | 
|---|
| 96 | const char _slnProjDepBeg[]     = "\n\tProjectSection(ProjectDependencies) = postProject"; | 
|---|
| 97 | const char _slnProjDepEnd[]     = "\n\tEndProjectSection"; | 
|---|
| 98 | const char _slnProjConfBeg[]    = "\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"; | 
|---|
| 99 | const char _slnProjRelConfTag1[]= ".Release|%1.ActiveCfg = Release|"; | 
|---|
| 100 | const char _slnProjRelConfTag2[]= ".Release|%1.Build.0 = Release|"; | 
|---|
| 101 | const char _slnProjDbgConfTag1[]= ".Debug|%1.ActiveCfg = Debug|"; | 
|---|
| 102 | const char _slnProjDbgConfTag2[]= ".Debug|%1.Build.0 = Debug|"; | 
|---|
| 103 | const char _slnProjConfEnd[]    = "\n\tEndGlobalSection"; | 
|---|
| 104 | const char _slnExtSections[]    = "\n\tGlobalSection(ExtensibilityGlobals) = postSolution" | 
|---|
| 105 | "\n\tEndGlobalSection" | 
|---|
| 106 | "\n\tGlobalSection(ExtensibilityAddIns) = postSolution" | 
|---|
| 107 | "\n\tEndGlobalSection"; | 
|---|
| 108 | // ------------------------------------------------------------------ | 
|---|
| 109 |  | 
|---|
| 110 | VcprojGenerator::VcprojGenerator() | 
|---|
| 111 | : Win32MakefileGenerator(), | 
|---|
| 112 | is64Bit(false), | 
|---|
| 113 | customBuildToolFilterFileSuffix(QStringLiteral( ".cbt")), | 
|---|
| 114 | projectWriter(nullptr) | 
|---|
| 115 | { | 
|---|
| 116 | } | 
|---|
| 117 |  | 
|---|
| 118 | VcprojGenerator::~VcprojGenerator() | 
|---|
| 119 | { | 
|---|
| 120 | delete projectWriter; | 
|---|
| 121 | } | 
|---|
| 122 |  | 
|---|
| 123 | bool VcprojGenerator::writeMakefile(QTextStream &t) | 
|---|
| 124 | { | 
|---|
| 125 | initProject(); // Fills the whole project with proper data | 
|---|
| 126 |  | 
|---|
| 127 | // Generate solution file | 
|---|
| 128 | if(project->first( "TEMPLATE") == "vcsubdirs") { | 
|---|
| 129 | if (!project->isActiveConfig( "build_pass")) { | 
|---|
| 130 | debug_msg(1, "Generator: MSVC.NET: Writing solution file"); | 
|---|
| 131 | writeSubDirs(t); | 
|---|
| 132 | } else { | 
|---|
| 133 | debug_msg(1, "Generator: MSVC.NET: Not writing solution file for build_pass configs"); | 
|---|
| 134 | } | 
|---|
| 135 | return true; | 
|---|
| 136 | } else | 
|---|
| 137 | // Generate single configuration project file | 
|---|
| 138 | if (project->first( "TEMPLATE") == "vcapp"|| | 
|---|
| 139 | project->first( "TEMPLATE") == "vclib") { | 
|---|
| 140 | if(!project->isActiveConfig( "build_pass")) { | 
|---|
| 141 | debug_msg(1, "Generator: MSVC.NET: Writing single configuration project file"); | 
|---|
| 142 | XmlOutput xmlOut(t); | 
|---|
| 143 | projectWriter->write(xmlOut, vcProject); | 
|---|
| 144 | } | 
|---|
| 145 | return true; | 
|---|
| 146 | } | 
|---|
| 147 | return project->isActiveConfig( "build_pass"); | 
|---|
| 148 | } | 
|---|
| 149 |  | 
|---|
| 150 | bool VcprojGenerator::writeProjectMakefile() | 
|---|
| 151 | { | 
|---|
| 152 | QTextStream t(&Option::output); | 
|---|
| 153 |  | 
|---|
| 154 | // Check if all requirements are fulfilled | 
|---|
| 155 | if(!project->values( "QMAKE_FAILED_REQUIREMENTS").isEmpty()) { | 
|---|
| 156 | fprintf(stderr, "Project file not generated because all requirements not met:\n\t%s\n", | 
|---|
| 157 | var( "QMAKE_FAILED_REQUIREMENTS").toLatin1().constData()); | 
|---|
| 158 | return true; | 
|---|
| 159 | } | 
|---|
| 160 |  | 
|---|
| 161 | // Generate project file | 
|---|
| 162 | if(project->first( "TEMPLATE") == "vcapp"|| | 
|---|
| 163 | project->first( "TEMPLATE") == "vclib") { | 
|---|
| 164 | if (!mergedProjects.count()) { | 
|---|
| 165 | warn_msg(WarnLogic, "Generator: MSVC.NET: no single configuration created, cannot output project!"); | 
|---|
| 166 | return false; | 
|---|
| 167 | } | 
|---|
| 168 |  | 
|---|
| 169 | debug_msg(1, "Generator: MSVC.NET: Writing project file"); | 
|---|
| 170 | VCProject mergedProject; | 
|---|
| 171 | for (int i = 0; i < mergedProjects.count(); ++i) { | 
|---|
| 172 | VCProjectSingleConfig *singleProject = &(mergedProjects.at(i)->vcProject); | 
|---|
| 173 | mergedProject.SingleProjects += *singleProject; | 
|---|
| 174 | for (int j = 0; j < singleProject->ExtraCompilersFiles.count(); ++j) { | 
|---|
| 175 | const QString &compilerName = singleProject->ExtraCompilersFiles.at(j).Name; | 
|---|
| 176 | if (!mergedProject.ExtraCompilers.contains(compilerName)) | 
|---|
| 177 | mergedProject.ExtraCompilers += compilerName; | 
|---|
| 178 | } | 
|---|
| 179 | } | 
|---|
| 180 |  | 
|---|
| 181 | if(mergedProjects.count() > 1 && | 
|---|
| 182 | mergedProjects.at(0)->vcProject.Name == | 
|---|
| 183 | mergedProjects.at(1)->vcProject.Name) | 
|---|
| 184 | mergedProjects.at(0)->writePrlFile(); | 
|---|
| 185 | mergedProject.Name = project->first( "QMAKE_PROJECT_NAME").toQString(); | 
|---|
| 186 | mergedProject.Version = mergedProjects.at(0)->vcProject.Version; | 
|---|
| 187 | mergedProject.SdkVersion = mergedProjects.at(0)->vcProject.SdkVersion; | 
|---|
| 188 | mergedProject.ProjectGUID = project->isEmpty( "QMAKE_UUID") ? getProjectUUID().toString().toUpper() : project->first( "QMAKE_UUID").toQString(); | 
|---|
| 189 | mergedProject.Keyword = project->first( "VCPROJ_KEYWORD").toQString(); | 
|---|
| 190 | mergedProject.SccProjectName = mergedProjects.at(0)->vcProject.SccProjectName; | 
|---|
| 191 | mergedProject.SccLocalPath = mergedProjects.at(0)->vcProject.SccLocalPath; | 
|---|
| 192 | mergedProject.PlatformName = mergedProjects.at(0)->vcProject.PlatformName; | 
|---|
| 193 | mergedProject.WindowsTargetPlatformVersion = | 
|---|
| 194 | project->first( "WINDOWS_TARGET_PLATFORM_VERSION").toQString(); | 
|---|
| 195 | mergedProject.WindowsTargetPlatformMinVersion = | 
|---|
| 196 | project->first( "WINDOWS_TARGET_PLATFORM_MIN_VERSION").toQString(); | 
|---|
| 197 |  | 
|---|
| 198 | XmlOutput xmlOut(t); | 
|---|
| 199 | projectWriter->write(xmlOut, mergedProject); | 
|---|
| 200 | return true; | 
|---|
| 201 | } else if(project->first( "TEMPLATE") == "vcsubdirs") { | 
|---|
| 202 | return writeMakefile(t); | 
|---|
| 203 | } | 
|---|
| 204 | return false; | 
|---|
| 205 | } | 
|---|
| 206 |  | 
|---|
| 207 | struct VcsolutionDepend { | 
|---|
| 208 | QString uuid; | 
|---|
| 209 | QString vcprojFile; | 
|---|
| 210 | QString projectName; | 
|---|
| 211 | QString target; | 
|---|
| 212 | Target targetType; | 
|---|
| 213 | QStringList dependencies; | 
|---|
| 214 | }; | 
|---|
| 215 |  | 
|---|
| 216 | /* Disable optimization in getProjectUUID() due to a compiler | 
|---|
| 217 | * bug in MSVC 2015 that causes ASSERT: "&other != this" in the QString | 
|---|
| 218 | * copy constructor for non-empty file names at: | 
|---|
| 219 | * filename.isEmpty()?project->first("QMAKE_MAKEFILE"):filename */ | 
|---|
| 220 |  | 
|---|
| 221 | #if defined(Q_CC_MSVC) && !defined(Q_CC_CLANG) | 
|---|
| 222 | #   pragma optimize( "g", off ) | 
|---|
| 223 | #   pragma warning ( disable : 4748 ) | 
|---|
| 224 | #endif | 
|---|
| 225 |  | 
|---|
| 226 | QUuid VcprojGenerator::getProjectUUID(const QString &filename) | 
|---|
| 227 | { | 
|---|
| 228 | bool validUUID = true; | 
|---|
| 229 |  | 
|---|
| 230 | // Read GUID from variable-space | 
|---|
| 231 | auto uuid = QUuid::fromString(project->first( "GUID").toQStringView()); | 
|---|
| 232 |  | 
|---|
| 233 | // If none, create one based on the MD5 of absolute project path | 
|---|
| 234 | if(uuid.isNull() || !filename.isEmpty()) { | 
|---|
| 235 | QString abspath = Option::fixPathToTargetOS( | 
|---|
| 236 | filename.isEmpty() ? project->first( "QMAKE_MAKEFILE").toQString() : filename); | 
|---|
| 237 | QByteArray digest = QCryptographicHash::hash(abspath.toUtf8(), QCryptographicHash::Sha1); | 
|---|
| 238 | memcpy((unsigned char*)(&uuid), digest.constData(), sizeof(QUuid)); | 
|---|
| 239 | validUUID = !uuid.isNull(); | 
|---|
| 240 | uuid.data4[0] = (uuid.data4[0] & 0x3F) | 0x80; // UV_DCE variant | 
|---|
| 241 | uuid.data3 = (uuid.data3 & 0x0FFF) | (QUuid::Name<<12); | 
|---|
| 242 | } | 
|---|
| 243 |  | 
|---|
| 244 | // If still not valid, generate new one, and suggest adding to .pro | 
|---|
| 245 | if(uuid.isNull() || !validUUID) { | 
|---|
| 246 | uuid = QUuid::createUuid(); | 
|---|
| 247 | fprintf(stderr, | 
|---|
| 248 | "qmake couldn't create a GUID based on filepath, and we couldn't\nfind a valid GUID in the .pro file (Consider adding\n'GUID = %s'  to the .pro file)\n", | 
|---|
| 249 | uuid.toString().toUpper().toLatin1().constData()); | 
|---|
| 250 | } | 
|---|
| 251 |  | 
|---|
| 252 | // Store GUID in variable-space | 
|---|
| 253 | project->values( "GUID") = ProStringList(uuid.toString().toUpper()); | 
|---|
| 254 | return uuid; | 
|---|
| 255 | } | 
|---|
| 256 |  | 
|---|
| 257 | #if defined(Q_CC_MSVC) && !defined(Q_CC_CLANG) | 
|---|
| 258 | #   pragma optimize( "g", on ) | 
|---|
| 259 | #endif | 
|---|
| 260 |  | 
|---|
| 261 | QUuid VcprojGenerator::increaseUUID(const QUuid &id) | 
|---|
| 262 | { | 
|---|
| 263 | QUuid result(id); | 
|---|
| 264 | qint64 dataFirst = (result.data4[0] << 24) + | 
|---|
| 265 | (result.data4[1] << 16) + | 
|---|
| 266 | (result.data4[2] << 8) + | 
|---|
| 267 | result.data4[3]; | 
|---|
| 268 | qint64 dataLast =  (result.data4[4] << 24) + | 
|---|
| 269 | (result.data4[5] << 16) + | 
|---|
| 270 | (result.data4[6] <<  8) + | 
|---|
| 271 | result.data4[7]; | 
|---|
| 272 |  | 
|---|
| 273 | if(!(dataLast++)) | 
|---|
| 274 | dataFirst++; | 
|---|
| 275 |  | 
|---|
| 276 | result.data4[0] = uchar((dataFirst >> 24) & 0xff); | 
|---|
| 277 | result.data4[1] = uchar((dataFirst >> 16) & 0xff); | 
|---|
| 278 | result.data4[2] = uchar((dataFirst >>  8) & 0xff); | 
|---|
| 279 | result.data4[3] = uchar(dataFirst         & 0xff); | 
|---|
| 280 | result.data4[4] = uchar((dataLast  >> 24) & 0xff); | 
|---|
| 281 | result.data4[5] = uchar((dataLast  >> 16) & 0xff); | 
|---|
| 282 | result.data4[6] = uchar((dataLast  >>  8) & 0xff); | 
|---|
| 283 | result.data4[7] = uchar(dataLast          & 0xff); | 
|---|
| 284 | return result; | 
|---|
| 285 | } | 
|---|
| 286 |  | 
|---|
| 287 | QString VcprojGenerator::retrievePlatformToolSet() const | 
|---|
| 288 | { | 
|---|
| 289 | // The PlatformToolset string corresponds to the name of a directory in | 
|---|
| 290 | // $(VCTargetsPath)\Platforms\{Win32,x64,...}\PlatformToolsets | 
|---|
| 291 | // e.g. v90, v100, v110, v110_xp, v120_CTP_Nov, v120, or WindowsSDK7.1 | 
|---|
| 292 |  | 
|---|
| 293 | // This environment variable may be set by a commandline build | 
|---|
| 294 | // environment such as the Windows SDK command prompt | 
|---|
| 295 | QByteArray envVar = qgetenv( "PlatformToolset"); | 
|---|
| 296 | if (!envVar.isEmpty()) | 
|---|
| 297 | return envVar; | 
|---|
| 298 |  | 
|---|
| 299 | switch (vcProject.Configuration.CompilerVersion) | 
|---|
| 300 | { | 
|---|
| 301 | case NET2012: | 
|---|
| 302 | return QStringLiteral( "v110"); | 
|---|
| 303 | case NET2013: | 
|---|
| 304 | return QStringLiteral( "v120"); | 
|---|
| 305 | case NET2015: | 
|---|
| 306 | return QStringLiteral( "v140"); | 
|---|
| 307 | case NET2017: | 
|---|
| 308 | return QStringLiteral( "v141"); | 
|---|
| 309 | case NET2019: | 
|---|
| 310 | return QStringLiteral( "v142"); | 
|---|
| 311 | default: | 
|---|
| 312 | return QString(); | 
|---|
| 313 | } | 
|---|
| 314 | } | 
|---|
| 315 |  | 
|---|
| 316 | bool VcprojGenerator::isStandardSuffix(const QString &suffix) const | 
|---|
| 317 | { | 
|---|
| 318 | if (!project->values( "QMAKE_APP_FLAG").isEmpty()) { | 
|---|
| 319 | if (suffix.compare( "exe", Qt::CaseInsensitive) == 0) | 
|---|
| 320 | return true; | 
|---|
| 321 | } else if (project->isActiveConfig( "shared")) { | 
|---|
| 322 | if (suffix.compare( "dll", Qt::CaseInsensitive) == 0) | 
|---|
| 323 | return true; | 
|---|
| 324 | } else { | 
|---|
| 325 | if (suffix.compare( "lib", Qt::CaseInsensitive) == 0) | 
|---|
| 326 | return true; | 
|---|
| 327 | } | 
|---|
| 328 | return false; | 
|---|
| 329 | } | 
|---|
| 330 |  | 
|---|
| 331 | ProString VcprojGenerator::firstInputFileName(const ProString &) const | 
|---|
| 332 | { | 
|---|
| 333 | for (const ProString &var : project->values(ProKey(extraCompilerName + ".input"))) { | 
|---|
| 334 | const ProStringList &files = project->values(var.toKey()); | 
|---|
| 335 | if (!files.isEmpty()) | 
|---|
| 336 | return files.first(); | 
|---|
| 337 | } | 
|---|
| 338 | return ProString(); | 
|---|
| 339 | } | 
|---|
| 340 |  | 
|---|
| 341 | QString VcprojGenerator::firstExpandedOutputFileName(const ProString &) | 
|---|
| 342 | { | 
|---|
| 343 | const ProString firstOutput = project->first(ProKey(extraCompilerName + ".output")); | 
|---|
| 344 | return replaceExtraCompilerVariables(firstOutput.toQString(), | 
|---|
| 345 | firstInputFileName(extraCompilerName).toQString(), | 
|---|
| 346 | QString(), NoShell); | 
|---|
| 347 | } | 
|---|
| 348 |  | 
|---|
| 349 | ProStringList VcprojGenerator::collectDependencies(QMakeProject *proj, QHash<QString, QString> &projLookup, | 
|---|
| 350 | QHash<QString, QString> &projGuids, | 
|---|
| 351 | QHash<VcsolutionDepend *, QStringList> &, | 
|---|
| 352 | QHash<QString, VcsolutionDepend*> &solution_depends, | 
|---|
| 353 | QList<VcsolutionDepend*> &solution_cleanup, | 
|---|
| 354 | QTextStream &t, | 
|---|
| 355 | QHash<QString, ProStringList> &subdirProjectLookup, | 
|---|
| 356 | const ProStringList &allDependencies) | 
|---|
| 357 | { | 
|---|
| 358 | QList<QPair<QString, ProStringList>> collectedSubdirs; | 
|---|
| 359 | ProStringList tmp_proj_subdirs = proj->values( "SUBDIRS"); | 
|---|
| 360 | ProStringList projectsInProject; | 
|---|
| 361 | const int numSubdirs = tmp_proj_subdirs.size(); | 
|---|
| 362 | collectedSubdirs.reserve(numSubdirs); | 
|---|
| 363 | for (int x = 0; x < numSubdirs; ++x) { | 
|---|
| 364 | ProString tmpdir = tmp_proj_subdirs.at(x); | 
|---|
| 365 | const ProKey tmpdirConfig(tmpdir + ".CONFIG"); | 
|---|
| 366 | if (!proj->isEmpty(tmpdirConfig)) { | 
|---|
| 367 | const ProStringList config = proj->values(tmpdirConfig); | 
|---|
| 368 | if (config.contains(QStringLiteral( "no_default_target"))) | 
|---|
| 369 | continue; // Ignore this sub-dir | 
|---|
| 370 | } | 
|---|
| 371 | const ProKey fkey(tmpdir + ".file"); | 
|---|
| 372 | const ProKey skey(tmpdir + ".subdir"); | 
|---|
| 373 | if (!proj->isEmpty(fkey)) { | 
|---|
| 374 | if (!proj->isEmpty(skey)) | 
|---|
| 375 | warn_msg(WarnLogic, "Cannot assign both file and subdir for subdir %s", | 
|---|
| 376 | tmpdir.toLatin1().constData()); | 
|---|
| 377 | tmpdir = proj->first(fkey); | 
|---|
| 378 | } else if (!proj->isEmpty(skey)) { | 
|---|
| 379 | tmpdir = proj->first(skey); | 
|---|
| 380 | } | 
|---|
| 381 | projectsInProject.append(tmpdir); | 
|---|
| 382 | collectedSubdirs.append(qMakePair(tmpdir.toQString(), proj->values(ProKey(tmp_proj_subdirs.at(x) + ".depends")))); | 
|---|
| 383 | projLookup.insert(tmp_proj_subdirs.at(x).toQString(), tmpdir.toQString()); | 
|---|
| 384 | } | 
|---|
| 385 | for (const auto &subdir : qAsConst(collectedSubdirs)) { | 
|---|
| 386 | QString profile = subdir.first; | 
|---|
| 387 | QFileInfo fi(fileInfo(Option::normalizePath(profile))); | 
|---|
| 388 | if (fi.exists()) { | 
|---|
| 389 | if (fi.isDir()) { | 
|---|
| 390 | if (!profile.endsWith(Option::dir_sep)) | 
|---|
| 391 | profile += Option::dir_sep; | 
|---|
| 392 | profile += fi.baseName() + Option::pro_ext; | 
|---|
| 393 | QString profileKey = fi.absoluteFilePath(); | 
|---|
| 394 | fi = QFileInfo(fileInfo(Option::normalizePath(profile))); | 
|---|
| 395 | if (!fi.exists()) | 
|---|
| 396 | continue; | 
|---|
| 397 | projLookup.insert(profileKey, fi.absoluteFilePath()); | 
|---|
| 398 | } | 
|---|
| 399 | QString oldpwd = qmake_getpwd(); | 
|---|
| 400 | QString oldoutpwd = Option::output_dir; | 
|---|
| 401 | QMakeProject tmp_proj; | 
|---|
| 402 | QString dir = fi.absolutePath(), fn = fi.fileName(); | 
|---|
| 403 | if (!dir.isEmpty()) { | 
|---|
| 404 | if (!qmake_setpwd(dir)) | 
|---|
| 405 | fprintf(stderr, "Cannot find directory: %s", dir.toLatin1().constData()); | 
|---|
| 406 | } | 
|---|
| 407 | Option::output_dir = Option::globals->shadowedPath(QDir::cleanPath(dir)); | 
|---|
| 408 | if (tmp_proj.read(fn)) { | 
|---|
| 409 | // Check if all requirements are fulfilled | 
|---|
| 410 | if (!tmp_proj.isEmpty( "QMAKE_FAILED_REQUIREMENTS")) { | 
|---|
| 411 | fprintf(stderr, "Project file(%s) not added to Solution because all requirements not met:\n\t%s\n", | 
|---|
| 412 | fn.toLatin1().constData(), | 
|---|
| 413 | tmp_proj.values( "QMAKE_FAILED_REQUIREMENTS").join(' ').toLatin1().constData()); | 
|---|
| 414 | qmake_setpwd(oldpwd); | 
|---|
| 415 | Option::output_dir = oldoutpwd; | 
|---|
| 416 | continue; | 
|---|
| 417 | } | 
|---|
| 418 | if (tmp_proj.first( "TEMPLATE") == "vcsubdirs") { | 
|---|
| 419 | ProStringList tmpList = collectDependencies(&tmp_proj, projLookup, projGuids, extraSubdirs, solution_depends, solution_cleanup, t, subdirProjectLookup, subdir.second); | 
|---|
| 420 | subdirProjectLookup.insert(subdir.first, tmpList); | 
|---|
| 421 | } else { | 
|---|
| 422 | ProStringList tmpList; | 
|---|
| 423 | tmpList += subdir.second; | 
|---|
| 424 | tmpList += allDependencies; | 
|---|
| 425 | // Initialize a 'fake' project to get the correct variables | 
|---|
| 426 | // and to be able to extract all the dependencies | 
|---|
| 427 | Option::QMAKE_MODE old_mode = Option::qmake_mode; | 
|---|
| 428 | Option::qmake_mode = Option::QMAKE_GENERATE_NOTHING; | 
|---|
| 429 | VcprojGenerator tmp_vcproj; | 
|---|
| 430 | tmp_vcproj.setNoIO(true); | 
|---|
| 431 | tmp_vcproj.setProjectFile(&tmp_proj); | 
|---|
| 432 | Option::qmake_mode = old_mode; | 
|---|
| 433 |  | 
|---|
| 434 | // We assume project filename is [QMAKE_PROJECT_NAME].vcproj | 
|---|
| 435 | const ProString projectName = tmp_vcproj.project->first( "QMAKE_PROJECT_NAME"); | 
|---|
| 436 | const QString vcproj = projectName + project->first( "VCPROJ_EXTENSION"); | 
|---|
| 437 | QString vcprojDir = Option::output_dir; | 
|---|
| 438 |  | 
|---|
| 439 | // If file doesn't exsist, then maybe the users configuration | 
|---|
| 440 | // doesn't allow it to be created. Skip to next... | 
|---|
| 441 | if (!exists(vcprojDir + Option::dir_sep + vcproj)) { | 
|---|
| 442 | warn_msg(WarnLogic, "Ignored (not found) '%s'", QString(vcprojDir + Option::dir_sep + vcproj).toLatin1().constData()); | 
|---|
| 443 | goto nextfile; // # Dirty! | 
|---|
| 444 | } | 
|---|
| 445 |  | 
|---|
| 446 | VcsolutionDepend *newDep = new VcsolutionDepend; | 
|---|
| 447 | newDep->vcprojFile = vcprojDir + Option::dir_sep + vcproj; | 
|---|
| 448 | newDep->projectName = projectName.toQString(); | 
|---|
| 449 | newDep->target = tmp_proj.first( "MSVCPROJ_TARGET").toQString().section(Option::dir_sep, -1); | 
|---|
| 450 | newDep->targetType = tmp_vcproj.projectTarget; | 
|---|
| 451 | newDep->uuid = tmp_proj.isEmpty( "QMAKE_UUID") ? getProjectUUID(Option::fixPathToLocalOS(vcprojDir + QDir::separator() + vcproj)).toString().toUpper(): tmp_proj.first( "QMAKE_UUID").toQString(); | 
|---|
| 452 | // We want to store it as the .lib name. | 
|---|
| 453 | if (newDep->target.endsWith( ".dll")) | 
|---|
| 454 | newDep->target = newDep->target.left(newDep->target.length()-3) + "lib"; | 
|---|
| 455 | projGuids.insert(newDep->projectName, newDep->target); | 
|---|
| 456 |  | 
|---|
| 457 | if (tmpList.size()) { | 
|---|
| 458 | const ProStringList depends = tmpList; | 
|---|
| 459 | for (const ProString &dep : depends) { | 
|---|
| 460 | QString depend = dep.toQString(); | 
|---|
| 461 | if (!projGuids[depend].isEmpty()) { | 
|---|
| 462 | newDep->dependencies << projGuids[depend]; | 
|---|
| 463 | } else if (subdirProjectLookup[projLookup[depend]].size() > 0) { | 
|---|
| 464 | const ProStringList tmpLst = subdirProjectLookup[projLookup[depend]]; | 
|---|
| 465 | for (const ProString &tDep : tmpLst) { | 
|---|
| 466 | QString tmpDep = tDep.toQString(); | 
|---|
| 467 | newDep->dependencies << projGuids[projLookup[tmpDep]]; | 
|---|
| 468 | } | 
|---|
| 469 | } else { | 
|---|
| 470 | extraSubdirs.insert(newDep, tmpList.toQStringList()); | 
|---|
| 471 | newDep->dependencies.clear(); | 
|---|
| 472 | break; | 
|---|
| 473 | } | 
|---|
| 474 | } | 
|---|
| 475 | } | 
|---|
| 476 |  | 
|---|
| 477 | // All ActiveQt Server projects are dependent on idc.exe | 
|---|
| 478 | if (tmp_proj.values( "CONFIG").contains( "qaxserver")) | 
|---|
| 479 | newDep->dependencies << "idc.exe"; | 
|---|
| 480 |  | 
|---|
| 481 | // Add all unknown libs to the deps | 
|---|
| 482 | QStringList where = QStringList() << "LIBS"<< "LIBS_PRIVATE" | 
|---|
| 483 | << "QMAKE_LIBS"<< "QMAKE_LIBS_PRIVATE"; | 
|---|
| 484 | for (QStringList::ConstIterator wit = where.cbegin(); | 
|---|
| 485 | wit != where.cend(); ++wit) { | 
|---|
| 486 | const ProStringList &l = tmp_proj.values(ProKey(*wit)); | 
|---|
| 487 | for (ProStringList::ConstIterator it = l.begin(); it != l.end(); ++it) { | 
|---|
| 488 | const QString opt = fixLibFlag(*it).toQString(); | 
|---|
| 489 | if (!opt.startsWith( "/") &&   // Not a switch | 
|---|
| 490 | opt != newDep->target && // Not self | 
|---|
| 491 | opt != "opengl32.lib"&& // We don't care about these libs | 
|---|
| 492 | opt != "glu32.lib"&&    // to make depgen alittle faster | 
|---|
| 493 | opt != "kernel32.lib"&& | 
|---|
| 494 | opt != "user32.lib"&& | 
|---|
| 495 | opt != "gdi32.lib"&& | 
|---|
| 496 | opt != "comdlg32.lib"&& | 
|---|
| 497 | opt != "advapi32.lib"&& | 
|---|
| 498 | opt != "shell32.lib"&& | 
|---|
| 499 | opt != "ole32.lib"&& | 
|---|
| 500 | opt != "oleaut32.lib"&& | 
|---|
| 501 | opt != "uuid.lib"&& | 
|---|
| 502 | opt != "imm32.lib"&& | 
|---|
| 503 | opt != "winmm.lib"&& | 
|---|
| 504 | opt != "wsock32.lib"&& | 
|---|
| 505 | opt != "ws2_32.lib"&& | 
|---|
| 506 | opt != "winspool.lib"&& | 
|---|
| 507 | opt != "delayimp.lib") | 
|---|
| 508 | { | 
|---|
| 509 | newDep->dependencies << opt.section(Option::dir_sep, -1); | 
|---|
| 510 | } | 
|---|
| 511 | } | 
|---|
| 512 | } | 
|---|
| 513 | #ifdef DEBUG_SOLUTION_GEN | 
|---|
| 514 | qDebug( "Deps for %20s: [%s]", newDep->target.toLatin1().constData(), newDep->dependencies.join( " :: ").toLatin1().constData()); | 
|---|
| 515 | #endif | 
|---|
| 516 | solution_cleanup.append(newDep); | 
|---|
| 517 | solution_depends.insert(newDep->target, newDep); | 
|---|
| 518 | } | 
|---|
| 519 | nextfile: | 
|---|
| 520 | qmake_setpwd(oldpwd); | 
|---|
| 521 | Option::output_dir = oldoutpwd; | 
|---|
| 522 | } | 
|---|
| 523 | } | 
|---|
| 524 | } | 
|---|
| 525 | return projectsInProject; | 
|---|
| 526 | } | 
|---|
| 527 |  | 
|---|
| 528 | void VcprojGenerator::writeSubDirs(QTextStream &t) | 
|---|
| 529 | { | 
|---|
| 530 | // Check if all requirements are fulfilled | 
|---|
| 531 | if(!project->values( "QMAKE_FAILED_REQUIREMENTS").isEmpty()) { | 
|---|
| 532 | fprintf(stderr, "Project file not generated because all requirements not met:\n\t%s\n", | 
|---|
| 533 | var( "QMAKE_FAILED_REQUIREMENTS").toLatin1().constData()); | 
|---|
| 534 | return; | 
|---|
| 535 | } | 
|---|
| 536 |  | 
|---|
| 537 | switch (vcProject.Configuration.CompilerVersion) { | 
|---|
| 538 | case NET2019: | 
|---|
| 539 | t << _slnHeader142; | 
|---|
| 540 | break; | 
|---|
| 541 | case NET2017: | 
|---|
| 542 | t << _slnHeader141; | 
|---|
| 543 | break; | 
|---|
| 544 | case NET2015: | 
|---|
| 545 | t << _slnHeader140; | 
|---|
| 546 | break; | 
|---|
| 547 | case NET2013: | 
|---|
| 548 | t << _slnHeader120; | 
|---|
| 549 | break; | 
|---|
| 550 | case NET2012: | 
|---|
| 551 | t << _slnHeader110; | 
|---|
| 552 | break; | 
|---|
| 553 | case NET2010: | 
|---|
| 554 | t << _slnHeader100; | 
|---|
| 555 | break; | 
|---|
| 556 | case NET2008: | 
|---|
| 557 | t << _slnHeader90; | 
|---|
| 558 | break; | 
|---|
| 559 | case NET2005: | 
|---|
| 560 | t << _slnHeader80; | 
|---|
| 561 | break; | 
|---|
| 562 | case NET2003: | 
|---|
| 563 | t << _slnHeader71; | 
|---|
| 564 | break; | 
|---|
| 565 | case NET2002: | 
|---|
| 566 | t << _slnHeader70; | 
|---|
| 567 | break; | 
|---|
| 568 | default: | 
|---|
| 569 | t << _slnHeader70; | 
|---|
| 570 | warn_msg(WarnLogic, "Generator: MSVC.NET: Unknown version (%d) of MSVC detected for .sln", | 
|---|
| 571 | vcProject.Configuration.CompilerVersion); | 
|---|
| 572 | break; | 
|---|
| 573 | } | 
|---|
| 574 |  | 
|---|
| 575 | QHash<QString, VcsolutionDepend*> solution_depends; | 
|---|
| 576 | QList<VcsolutionDepend*> solution_cleanup; | 
|---|
| 577 |  | 
|---|
| 578 | // Make sure that all temp projects are configured | 
|---|
| 579 | // for release so that the depends are created | 
|---|
| 580 | // without the debug <lib>dxxx.lib name mangling | 
|---|
| 581 | QString old_after_vars = Option::globals->extra_cmds[QMakeEvalAfter]; | 
|---|
| 582 | Option::globals->extra_cmds[QMakeEvalAfter].append( "\nCONFIG+=release"); | 
|---|
| 583 |  | 
|---|
| 584 | QHash<QString, QString> profileLookup; | 
|---|
| 585 | QHash<QString, QString> projGuids; | 
|---|
| 586 | QHash<VcsolutionDepend *, QStringList> ; | 
|---|
| 587 | QHash<QString, ProStringList> subdirProjectLookup; | 
|---|
| 588 | collectDependencies(project, profileLookup, projGuids, extraSubdirs, solution_depends, solution_cleanup, t, subdirProjectLookup); | 
|---|
| 589 |  | 
|---|
| 590 | // write out projects | 
|---|
| 591 | for (QList<VcsolutionDepend*>::Iterator it = solution_cleanup.begin(); it != solution_cleanup.end(); ++it) { | 
|---|
| 592 | // ### quoting rules? | 
|---|
| 593 | t << _slnProjectBeg << _slnMSVCvcprojGUID << _slnProjectMid | 
|---|
| 594 | << "\""<< (*it)->projectName << "\", \""<< (*it)->vcprojFile | 
|---|
| 595 | << "\", \""<< (*it)->uuid << "\""; | 
|---|
| 596 |  | 
|---|
| 597 | debug_msg(1, "Project %s has dependencies: %s", (*it)->target.toLatin1().constData(), (*it)->dependencies.join( " ").toLatin1().constData()); | 
|---|
| 598 |  | 
|---|
| 599 | bool hasDependency = false; | 
|---|
| 600 | for (QStringList::iterator dit = (*it)->dependencies.begin();  dit != (*it)->dependencies.end(); ++dit) { | 
|---|
| 601 | if (VcsolutionDepend *vc = solution_depends[*dit]) { | 
|---|
| 602 | if (!hasDependency) { | 
|---|
| 603 | hasDependency = true; | 
|---|
| 604 | t << _slnProjDepBeg; | 
|---|
| 605 | } | 
|---|
| 606 | t << "\n\t\t"<< vc->uuid << " = "<< vc->uuid; | 
|---|
| 607 | } | 
|---|
| 608 | } | 
|---|
| 609 | if (hasDependency) | 
|---|
| 610 | t << _slnProjDepEnd; | 
|---|
| 611 |  | 
|---|
| 612 | t << _slnProjectEnd; | 
|---|
| 613 | } | 
|---|
| 614 |  | 
|---|
| 615 | t << _slnGlobalBeg; | 
|---|
| 616 |  | 
|---|
| 617 | for (auto  = extraSubdirs.cbegin(), end = extraSubdirs.cend(); extraIt != end; ++extraIt) { | 
|---|
| 618 | for (const QString &depend : extraIt.value()) { | 
|---|
| 619 | if (!projGuids[depend].isEmpty()) { | 
|---|
| 620 | extraIt.key()->dependencies << projGuids[depend]; | 
|---|
| 621 | } else if (!profileLookup[depend].isEmpty()) { | 
|---|
| 622 | if (!projGuids[profileLookup[depend]].isEmpty()) | 
|---|
| 623 | extraIt.key()->dependencies << projGuids[profileLookup[depend]]; | 
|---|
| 624 | } | 
|---|
| 625 | } | 
|---|
| 626 | } | 
|---|
| 627 | QString slnConf = _slnSolutionConf; | 
|---|
| 628 | if (!project->isEmpty( "VCPROJ_ARCH")) { | 
|---|
| 629 | slnConf.replace(QLatin1String( "|Win32"), "|"+ project->first( "VCPROJ_ARCH")); | 
|---|
| 630 | } else if (is64Bit) { | 
|---|
| 631 | slnConf.replace(QLatin1String( "|Win32"), QLatin1String( "|x64")); | 
|---|
| 632 | } | 
|---|
| 633 | t << slnConf; | 
|---|
| 634 |  | 
|---|
| 635 | // Restore previous after_user_var options | 
|---|
| 636 | Option::globals->extra_cmds[QMakeEvalAfter] = old_after_vars; | 
|---|
| 637 |  | 
|---|
| 638 | t << _slnProjConfBeg; | 
|---|
| 639 | for(QList<VcsolutionDepend*>::Iterator it = solution_cleanup.begin(); it != solution_cleanup.end(); ++it) { | 
|---|
| 640 | QString platform = is64Bit ? "x64": "Win32"; | 
|---|
| 641 | QString xplatform = platform; | 
|---|
| 642 | if (!project->isEmpty( "VCPROJ_ARCH")) { | 
|---|
| 643 | xplatform = project->first( "VCPROJ_ARCH").toQString(); | 
|---|
| 644 | } | 
|---|
| 645 | if (!project->isHostBuild()) | 
|---|
| 646 | platform = xplatform; | 
|---|
| 647 | t << "\n\t\t"<< (*it)->uuid << QString(_slnProjDbgConfTag1).arg(xplatform) << platform; | 
|---|
| 648 | t << "\n\t\t"<< (*it)->uuid << QString(_slnProjDbgConfTag2).arg(xplatform) << platform; | 
|---|
| 649 | t << "\n\t\t"<< (*it)->uuid << QString(_slnProjRelConfTag1).arg(xplatform) << platform; | 
|---|
| 650 | t << "\n\t\t"<< (*it)->uuid << QString(_slnProjRelConfTag2).arg(xplatform) << platform; | 
|---|
| 651 | } | 
|---|
| 652 | t << _slnProjConfEnd; | 
|---|
| 653 | t << _slnExtSections; | 
|---|
| 654 | t << _slnGlobalEnd; | 
|---|
| 655 |  | 
|---|
| 656 |  | 
|---|
| 657 | while (!solution_cleanup.isEmpty()) | 
|---|
| 658 | delete solution_cleanup.takeFirst(); | 
|---|
| 659 | } | 
|---|
| 660 |  | 
|---|
| 661 | // ------------------------------------------------------------------------------------------------ | 
|---|
| 662 | // ------------------------------------------------------------------------------------------------ | 
|---|
| 663 |  | 
|---|
| 664 | bool VcprojGenerator::hasBuiltinCompiler(const QString &file) | 
|---|
| 665 | { | 
|---|
| 666 | // Source files | 
|---|
| 667 | for (int i = 0; i < Option::cpp_ext.count(); ++i) | 
|---|
| 668 | if (file.endsWith(Option::cpp_ext.at(i))) | 
|---|
| 669 | return true; | 
|---|
| 670 | for (int i = 0; i < Option::c_ext.count(); ++i) | 
|---|
| 671 | if (file.endsWith(Option::c_ext.at(i))) | 
|---|
| 672 | return true; | 
|---|
| 673 | if (file.endsWith( ".rc") | 
|---|
| 674 | || file.endsWith( ".idl")) | 
|---|
| 675 | return true; | 
|---|
| 676 | return false; | 
|---|
| 677 | } | 
|---|
| 678 |  | 
|---|
| 679 | void VcprojGenerator::createCustomBuildToolFakeFile(const QString &cbtFilePath, | 
|---|
| 680 | const QString &realOutFilePath) | 
|---|
| 681 | { | 
|---|
| 682 | QFile file(fileFixify(cbtFilePath, FileFixifyFromOutdir | FileFixifyAbsolute)); | 
|---|
| 683 | if (file.exists()) | 
|---|
| 684 | return; | 
|---|
| 685 | if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { | 
|---|
| 686 | warn_msg(WarnLogic, "Cannot create '%s'.", qPrintable(file.fileName())); | 
|---|
| 687 | return; | 
|---|
| 688 | } | 
|---|
| 689 | file.write( "This is a dummy file needed to create "); | 
|---|
| 690 | file.write(qPrintable(realOutFilePath)); | 
|---|
| 691 | file.write( "\n"); | 
|---|
| 692 | } | 
|---|
| 693 |  | 
|---|
| 694 | void VcprojGenerator::init() | 
|---|
| 695 | { | 
|---|
| 696 | is64Bit = (project->first( "QMAKE_TARGET.arch") == "x86_64"); | 
|---|
| 697 | projectWriter = createProjectWriter(); | 
|---|
| 698 |  | 
|---|
| 699 | if(project->first( "TEMPLATE") == "vcsubdirs") //too much work for subdirs | 
|---|
| 700 | return; | 
|---|
| 701 |  | 
|---|
| 702 | debug_msg(1, "Generator: MSVC.NET: Initializing variables"); | 
|---|
| 703 |  | 
|---|
| 704 | // this should probably not be here, but I'm using it to wrap the .t files | 
|---|
| 705 | if (project->first( "TEMPLATE") == "vcapp") | 
|---|
| 706 | project->values( "QMAKE_APP_FLAG").append( "1"); | 
|---|
| 707 | else if (project->first( "TEMPLATE") == "vclib") | 
|---|
| 708 | project->values( "QMAKE_LIB_FLAG").append( "1"); | 
|---|
| 709 |  | 
|---|
| 710 | processVars(); | 
|---|
| 711 |  | 
|---|
| 712 | // set /VERSION for EXE/DLL header | 
|---|
| 713 | ProString major_minor = project->first( "VERSION_PE_HEADER"); | 
|---|
| 714 | if (major_minor.isEmpty()) { | 
|---|
| 715 | ProString version = project->first( "VERSION"); | 
|---|
| 716 | if (!version.isEmpty()) { | 
|---|
| 717 | int firstDot = version.indexOf( "."); | 
|---|
| 718 | int secondDot = version.indexOf( ".", firstDot + 1); | 
|---|
| 719 | major_minor = version.left(secondDot); | 
|---|
| 720 | } | 
|---|
| 721 | } | 
|---|
| 722 | if (!major_minor.isEmpty()) | 
|---|
| 723 | project->values( "QMAKE_LFLAGS").append( "/VERSION:"+ major_minor); | 
|---|
| 724 |  | 
|---|
| 725 | MakefileGenerator::init(); | 
|---|
| 726 |  | 
|---|
| 727 | // $$QMAKE.. -> $$MSVCPROJ.. ------------------------------------- | 
|---|
| 728 | const ProStringList &incs = project->values( "INCLUDEPATH"); | 
|---|
| 729 | for (ProStringList::ConstIterator incit = incs.begin(); incit != incs.end(); ++incit) { | 
|---|
| 730 | QString inc = (*incit).toQString(); | 
|---|
| 731 | project->values( "MSVCPROJ_INCPATH").append( "-I"+ escapeFilePath(inc)); | 
|---|
| 732 | } | 
|---|
| 733 |  | 
|---|
| 734 | QString dest = Option::fixPathToTargetOS(project->first( "TARGET").toQString()) + project->first( "TARGET_EXT"); | 
|---|
| 735 | project->values( "MSVCPROJ_TARGET") = ProStringList(dest); | 
|---|
| 736 |  | 
|---|
| 737 | // DLL COPY ------------------------------------------------------ | 
|---|
| 738 | if (project->isActiveConfig( "dll") && !project->values( "DLLDESTDIR").isEmpty()) { | 
|---|
| 739 | const ProStringList &dlldirs = project->values( "DLLDESTDIR"); | 
|---|
| 740 | QString copydll( ""); | 
|---|
| 741 | ProStringList::ConstIterator dlldir; | 
|---|
| 742 | for (dlldir = dlldirs.begin(); dlldir != dlldirs.end(); ++dlldir) { | 
|---|
| 743 | if (!copydll.isEmpty()) | 
|---|
| 744 | copydll += " && "; | 
|---|
| 745 | copydll += "copy \"$(TargetPath)\" "+ escapeFilePath(*dlldir); | 
|---|
| 746 | } | 
|---|
| 747 |  | 
|---|
| 748 | QString deststr( "Copy "+ dest + " to "); | 
|---|
| 749 | for (dlldir = dlldirs.begin(); dlldir != dlldirs.end();) { | 
|---|
| 750 | deststr += *dlldir; | 
|---|
| 751 | ++dlldir; | 
|---|
| 752 | if (dlldir != dlldirs.end()) | 
|---|
| 753 | deststr += ", "; | 
|---|
| 754 | } | 
|---|
| 755 |  | 
|---|
| 756 | project->values( "MSVCPROJ_COPY_DLL").append(copydll); | 
|---|
| 757 | project->values( "MSVCPROJ_COPY_DLL_DESC").append(deststr); | 
|---|
| 758 | } | 
|---|
| 759 |  | 
|---|
| 760 | #if 0 | 
|---|
| 761 | // Verbose output if "-d -d"... | 
|---|
| 762 | qDebug( "Generator: MSVC.NET: List of current variables:"); | 
|---|
| 763 | for (ProValueMap::ConstIterator it = project->variables().begin(); it != project->variables().end(); ++it) | 
|---|
| 764 | qDebug( "Generator: MSVC.NET: %s => %s", qPrintable(it.key().toQString()), qPrintable(it.value().join( " | "))); | 
|---|
| 765 | #endif | 
|---|
| 766 |  | 
|---|
| 767 | // Figure out what we're trying to build | 
|---|
| 768 | if(project->first( "TEMPLATE") == "vcapp") { | 
|---|
| 769 | projectTarget = Application; | 
|---|
| 770 | } else if(project->first( "TEMPLATE") == "vclib") { | 
|---|
| 771 | if(project->isActiveConfig( "staticlib")) { | 
|---|
| 772 | project->values( "LIBS") += project->values( "RES_FILE"); | 
|---|
| 773 | projectTarget = StaticLib; | 
|---|
| 774 | } else | 
|---|
| 775 | projectTarget = SharedLib; | 
|---|
| 776 | } | 
|---|
| 777 |  | 
|---|
| 778 | // Setup PCH variables | 
|---|
| 779 | precompH = project->first( "PRECOMPILED_HEADER").toQString(); | 
|---|
| 780 | precompSource = project->first( "PRECOMPILED_SOURCE").toQString(); | 
|---|
| 781 | pchIsCFile = project->isActiveConfig( "precompile_header_c"); | 
|---|
| 782 | usePCH = !precompH.isEmpty() && (pchIsCFile || project->isActiveConfig( "precompile_header")); | 
|---|
| 783 | if (usePCH) { | 
|---|
| 784 | precompHFilename = fileInfo(precompH).fileName(); | 
|---|
| 785 | // Created files | 
|---|
| 786 | QString origTarget = project->first( "QMAKE_ORIG_TARGET").toQString(); | 
|---|
| 787 | precompObj = origTarget + Option::obj_ext; | 
|---|
| 788 | precompPch = origTarget + ".pch"; | 
|---|
| 789 | // Add PRECOMPILED_HEADER to HEADERS | 
|---|
| 790 | if (!project->values( "HEADERS").contains(precompH)) | 
|---|
| 791 | project->values( "HEADERS") += precompH; | 
|---|
| 792 | // Return to variable pool | 
|---|
| 793 | project->values( "PRECOMPILED_OBJECT") = ProStringList(precompObj); | 
|---|
| 794 | project->values( "PRECOMPILED_PCH")    = ProStringList(precompPch); | 
|---|
| 795 |  | 
|---|
| 796 | autogenPrecompSource = precompSource.isEmpty() && project->isActiveConfig( "autogen_precompile_source"); | 
|---|
| 797 | if (autogenPrecompSource) { | 
|---|
| 798 | precompSource = precompH | 
|---|
| 799 | + (pchIsCFile | 
|---|
| 800 | ? (Option::c_ext.count() ? Option::c_ext.at(0) : QLatin1String( ".c")) | 
|---|
| 801 | : (Option::cpp_ext.count() ? Option::cpp_ext.at(0) : QLatin1String( ".cpp"))); | 
|---|
| 802 | project->values( "GENERATED_SOURCES") += precompSource; | 
|---|
| 803 | } else if (!precompSource.isEmpty()) { | 
|---|
| 804 | project->values( "SOURCES") += precompSource; | 
|---|
| 805 | } | 
|---|
| 806 | } | 
|---|
| 807 |  | 
|---|
| 808 | // Add all input files for a custom compiler into a map for uniqueness, | 
|---|
| 809 | // unless the compiler is configure as a combined stage, then use the first one | 
|---|
| 810 | for (const ProString &quc : project->values( "QMAKE_EXTRA_COMPILERS")) { | 
|---|
| 811 | const ProStringList &invar = project->values(ProKey(quc + ".input")); | 
|---|
| 812 | const QString compiler_out = project->first(ProKey(quc + ".output")).toQString(); | 
|---|
| 813 | for (ProStringList::ConstIterator iit = invar.constBegin(); iit != invar.constEnd(); ++iit) { | 
|---|
| 814 | ProStringList fileList = project->values((*iit).toKey()); | 
|---|
| 815 | if (!fileList.isEmpty()) { | 
|---|
| 816 | if (project->values(ProKey(quc + ".CONFIG")).indexOf( "combine") != -1) | 
|---|
| 817 | fileList.erase(fileList.begin() + 1, fileList.end()); | 
|---|
| 818 | for (ProStringList::ConstIterator fit = fileList.constBegin(); fit != fileList.constEnd(); ++fit) { | 
|---|
| 819 | QString file = (*fit).toQString(); | 
|---|
| 820 | if (verifyExtraCompiler(quc, file)) { | 
|---|
| 821 | if (!hasBuiltinCompiler(file)) { | 
|---|
| 822 | extraCompilerSources[file] += quc.toQString(); | 
|---|
| 823 | } else { | 
|---|
| 824 | // Create a fake file foo.moc.cbt for the project view. | 
|---|
| 825 | // This prevents VS from complaining about a circular | 
|---|
| 826 | // dependency from foo.moc -> foo.moc. | 
|---|
| 827 | QString realOut = replaceExtraCompilerVariables( | 
|---|
| 828 | compiler_out, file, QString(), NoShell); | 
|---|
| 829 | QString out = realOut + customBuildToolFilterFileSuffix; | 
|---|
| 830 | createCustomBuildToolFakeFile(out, realOut); | 
|---|
| 831 | out = Option::fixPathToTargetOS(out, false); | 
|---|
| 832 | extraCompilerSources[out] += quc.toQString(); | 
|---|
| 833 | extraCompilerOutputs[out] = file; | 
|---|
| 834 | } | 
|---|
| 835 | } | 
|---|
| 836 | } | 
|---|
| 837 | } | 
|---|
| 838 | } | 
|---|
| 839 | } | 
|---|
| 840 |  | 
|---|
| 841 | #if 0 // Debugging | 
|---|
| 842 | for (auto it = extraCompilerSources.cbegin(), end = extraCompilerSources.cend(); it != end; ++it) | 
|---|
| 843 | qDebug( "Extracompilers for %s are (%s)", it.key().toLatin1().constData(), it.value().join( ", ").toLatin1().constData()); | 
|---|
| 844 | for (auto it = extraCompilerOutputs.cbegin(), end = extraCompilerOutputs.cend(); it != end; ++it) | 
|---|
| 845 | qDebug( "Object mapping for %s is (%s)", it.key().toLatin1().constData(), it.value().join( ", ").toLatin1().constData()); | 
|---|
| 846 | qDebug( ""); | 
|---|
| 847 | #endif | 
|---|
| 848 | } | 
|---|
| 849 |  | 
|---|
| 850 | bool VcprojGenerator::mergeBuildProject(MakefileGenerator *other) | 
|---|
| 851 | { | 
|---|
| 852 | if (!other || !other->projectFile()) { | 
|---|
| 853 | warn_msg(WarnLogic, "VcprojGenerator: Cannot merge null project."); | 
|---|
| 854 | return false; | 
|---|
| 855 | } | 
|---|
| 856 | if (other->projectFile()->first( "MAKEFILE_GENERATOR") != project->first( "MAKEFILE_GENERATOR")) { | 
|---|
| 857 | warn_msg(WarnLogic, "VcprojGenerator: Cannot merge other types of projects! (ignored)"); | 
|---|
| 858 | return false; | 
|---|
| 859 | } | 
|---|
| 860 |  | 
|---|
| 861 | VcprojGenerator *otherVC = static_cast<VcprojGenerator*>(other); | 
|---|
| 862 | mergedProjects += otherVC; | 
|---|
| 863 | return true; | 
|---|
| 864 | } | 
|---|
| 865 |  | 
|---|
| 866 | void VcprojGenerator::initProject() | 
|---|
| 867 | { | 
|---|
| 868 | // Initialize XML sub elements | 
|---|
| 869 | // - Do this first since project elements may need | 
|---|
| 870 | // - to know of certain configuration options | 
|---|
| 871 | initConfiguration(); | 
|---|
| 872 | initRootFiles(); | 
|---|
| 873 | initSourceFiles(); | 
|---|
| 874 | initHeaderFiles(); | 
|---|
| 875 | initGeneratedFiles(); | 
|---|
| 876 | initLexYaccFiles(); | 
|---|
| 877 | initTranslationFiles(); | 
|---|
| 878 | initFormFiles(); | 
|---|
| 879 | initResourceFiles(); | 
|---|
| 880 | initDistributionFiles(); | 
|---|
| 881 | initExtraCompilerOutputs(); | 
|---|
| 882 |  | 
|---|
| 883 | // Own elements ----------------------------- | 
|---|
| 884 | vcProject.Name = project->first( "QMAKE_ORIG_TARGET").toQString(); | 
|---|
| 885 | switch (vcProject.Configuration.CompilerVersion) { | 
|---|
| 886 | case NET2019: | 
|---|
| 887 | vcProject.Version = "16.00"; | 
|---|
| 888 | break; | 
|---|
| 889 | case NET2017: | 
|---|
| 890 | vcProject.Version = "15.00"; | 
|---|
| 891 | break; | 
|---|
| 892 | case NET2015: | 
|---|
| 893 | vcProject.Version = "14.00"; | 
|---|
| 894 | break; | 
|---|
| 895 | case NET2013: | 
|---|
| 896 | vcProject.Version = "12.00"; | 
|---|
| 897 | break; | 
|---|
| 898 | case NET2012: | 
|---|
| 899 | vcProject.Version = "11.00"; | 
|---|
| 900 | break; | 
|---|
| 901 | case NET2010: | 
|---|
| 902 | vcProject.Version = "10.00"; | 
|---|
| 903 | break; | 
|---|
| 904 | case NET2008: | 
|---|
| 905 | vcProject.Version = "9,00"; | 
|---|
| 906 | break; | 
|---|
| 907 | case NET2005: | 
|---|
| 908 | //### using ',' because of a bug in 2005 B2 | 
|---|
| 909 | //### VS uses '.' or ',' depending on the regional settings! Using ',' always works. | 
|---|
| 910 | vcProject.Version = "8,00"; | 
|---|
| 911 | break; | 
|---|
| 912 | case NET2003: | 
|---|
| 913 | vcProject.Version = "7.10"; | 
|---|
| 914 | break; | 
|---|
| 915 | case NET2002: | 
|---|
| 916 | vcProject.Version = "7.00"; | 
|---|
| 917 | break; | 
|---|
| 918 | default: | 
|---|
| 919 | vcProject.Version = "7.00"; | 
|---|
| 920 | warn_msg(WarnLogic, "Generator: MSVC.NET: Unknown version (%d) of MSVC detected for .vcproj", vcProject.Configuration.CompilerVersion); | 
|---|
| 921 | break; | 
|---|
| 922 | } | 
|---|
| 923 |  | 
|---|
| 924 | vcProject.Keyword = project->first( "VCPROJ_KEYWORD").toQString(); | 
|---|
| 925 | if (!project->isEmpty( "VCPROJ_ARCH")) { | 
|---|
| 926 | vcProject.PlatformName = project->first( "VCPROJ_ARCH").toQString(); | 
|---|
| 927 | } else { | 
|---|
| 928 | vcProject.PlatformName = (is64Bit ? "x64": "Win32"); | 
|---|
| 929 | } | 
|---|
| 930 | vcProject.SdkVersion = project->first( "WINSDK_VER").toQString(); | 
|---|
| 931 | // These are not used by Qt, but may be used by customers | 
|---|
| 932 | vcProject.SccProjectName = project->first( "SCCPROJECTNAME").toQString(); | 
|---|
| 933 | vcProject.SccLocalPath = project->first( "SCCLOCALPATH").toQString(); | 
|---|
| 934 | vcProject.flat_files = project->isActiveConfig( "flat"); | 
|---|
| 935 |  | 
|---|
| 936 | // Set up the full target path for target conflict checking. | 
|---|
| 937 | const QChar slash = QLatin1Char('/'); | 
|---|
| 938 | QString destdir = QDir::fromNativeSeparators(var( "DESTDIR")); | 
|---|
| 939 | if (!destdir.endsWith(slash)) | 
|---|
| 940 | destdir.append(slash); | 
|---|
| 941 | project->values( "DEST_TARGET") = ProStringList(destdir | 
|---|
| 942 | + project->first( "TARGET") | 
|---|
| 943 | + project->first( "TARGET_EXT")); | 
|---|
| 944 | } | 
|---|
| 945 |  | 
|---|
| 946 | void VcprojGenerator::initConfiguration() | 
|---|
| 947 | { | 
|---|
| 948 | // Initialize XML sub elements | 
|---|
| 949 | // - Do this first since main configuration elements may need | 
|---|
| 950 | // - to know of certain compiler/linker options | 
|---|
| 951 | VCConfiguration &conf = vcProject.Configuration; | 
|---|
| 952 | conf.suppressUnknownOptionWarnings = project->isActiveConfig( "suppress_vcproj_warnings"); | 
|---|
| 953 | conf.CompilerVersion = vsVersionFromString(project->first( "MSVC_VER")); | 
|---|
| 954 |  | 
|---|
| 955 | initCompilerTool(); | 
|---|
| 956 |  | 
|---|
| 957 | // Only on configuration per build | 
|---|
| 958 | bool isDebug = project->isActiveConfig( "debug"); | 
|---|
| 959 |  | 
|---|
| 960 | if(projectTarget == StaticLib) | 
|---|
| 961 | initLibrarianTool(); | 
|---|
| 962 | else { | 
|---|
| 963 | conf.linker.GenerateDebugInformation = project->isActiveConfig( "debug_info") ? _True : _False; | 
|---|
| 964 | initLinkerTool(); | 
|---|
| 965 | } | 
|---|
| 966 | initManifestTool(); | 
|---|
| 967 | initResourceTool(); | 
|---|
| 968 | initIDLTool(); | 
|---|
| 969 |  | 
|---|
| 970 | // Own elements ----------------------------- | 
|---|
| 971 | ProString temp = project->first( "BuildBrowserInformation"); | 
|---|
| 972 | switch (projectTarget) { | 
|---|
| 973 | case SharedLib: | 
|---|
| 974 | conf.ConfigurationType = typeDynamicLibrary; | 
|---|
| 975 | break; | 
|---|
| 976 | case StaticLib: | 
|---|
| 977 | conf.ConfigurationType = typeStaticLibrary; | 
|---|
| 978 | break; | 
|---|
| 979 | case Application: | 
|---|
| 980 | default: | 
|---|
| 981 | conf.ConfigurationType = typeApplication; | 
|---|
| 982 | break; | 
|---|
| 983 | } | 
|---|
| 984 |  | 
|---|
| 985 | conf.OutputDirectory = project->first( "DESTDIR").toQString(); | 
|---|
| 986 | if (conf.OutputDirectory.isEmpty()) | 
|---|
| 987 | conf.OutputDirectory = ".\\"; | 
|---|
| 988 | if (!conf.OutputDirectory.endsWith( "\\")) | 
|---|
| 989 | conf.OutputDirectory += '\\'; | 
|---|
| 990 | if (conf.CompilerVersion >= NET2010) { | 
|---|
| 991 | conf.PlatformToolSet = retrievePlatformToolSet(); | 
|---|
| 992 |  | 
|---|
| 993 | const QFileInfo targetInfo = fileInfo(project->first( "MSVCPROJ_TARGET").toQString()); | 
|---|
| 994 | conf.PrimaryOutput = targetInfo.completeBaseName(); | 
|---|
| 995 |  | 
|---|
| 996 | const QString targetSuffix = targetInfo.suffix(); | 
|---|
| 997 | if (!isStandardSuffix(targetSuffix)) | 
|---|
| 998 | conf.PrimaryOutputExtension = '.' + targetSuffix; | 
|---|
| 999 | } | 
|---|
| 1000 |  | 
|---|
| 1001 | conf.Name = project->values( "BUILD_NAME").join(' '); | 
|---|
| 1002 | if (conf.Name.isEmpty()) | 
|---|
| 1003 | conf.Name = isDebug ? "Debug": "Release"; | 
|---|
| 1004 | conf.ConfigurationName = conf.Name; | 
|---|
| 1005 | if (!project->isEmpty( "VCPROJ_ARCH")) { | 
|---|
| 1006 | conf.Name += "|"+ project->first( "VCPROJ_ARCH"); | 
|---|
| 1007 | } else { | 
|---|
| 1008 | conf.Name += (is64Bit ? "|x64": "|Win32"); | 
|---|
| 1009 | } | 
|---|
| 1010 | conf.ATLMinimizesCRunTimeLibraryUsage = (project->first( "ATLMinimizesCRunTimeLibraryUsage").isEmpty() ? _False : _True); | 
|---|
| 1011 | conf.BuildBrowserInformation = triState(temp.isEmpty() ? (short)unset : temp.toShort()); | 
|---|
| 1012 | temp = project->first( "CharacterSet"); | 
|---|
| 1013 | conf.CharacterSet = charSet(temp.isEmpty() ? short(charSetNotSet) : temp.toShort()); | 
|---|
| 1014 | conf.DeleteExtensionsOnClean = project->first( "DeleteExtensionsOnClean").toQString(); | 
|---|
| 1015 | conf.ImportLibrary = conf.linker.ImportLibrary; | 
|---|
| 1016 | conf.IntermediateDirectory = project->first( "OBJECTS_DIR").toQString(); | 
|---|
| 1017 | conf.WholeProgramOptimization = conf.compiler.WholeProgramOptimization; | 
|---|
| 1018 | temp = project->first( "UseOfATL"); | 
|---|
| 1019 | if(!temp.isEmpty()) | 
|---|
| 1020 | conf.UseOfATL = useOfATL(temp.toShort()); | 
|---|
| 1021 | temp = project->first( "UseOfMfc"); | 
|---|
| 1022 | if(!temp.isEmpty()) | 
|---|
| 1023 | conf.UseOfMfc = useOfMfc(temp.toShort()); | 
|---|
| 1024 |  | 
|---|
| 1025 | // Configuration does not need parameters from | 
|---|
| 1026 | // these sub XML items; | 
|---|
| 1027 | initCustomBuildTool(); | 
|---|
| 1028 | initPreBuildEventTools(); | 
|---|
| 1029 | initPostBuildEventTools(); | 
|---|
| 1030 | // Only deploy for crosscompiled projects | 
|---|
| 1031 | if (!project->isHostBuild()) | 
|---|
| 1032 | initDeploymentTool(); | 
|---|
| 1033 | initWinDeployQtTool(); | 
|---|
| 1034 | initPreLinkEventTools(); | 
|---|
| 1035 | } | 
|---|
| 1036 |  | 
|---|
| 1037 | void VcprojGenerator::initCompilerTool() | 
|---|
| 1038 | { | 
|---|
| 1039 | QString placement = project->first( "OBJECTS_DIR").toQString(); | 
|---|
| 1040 | if(placement.isEmpty()) | 
|---|
| 1041 | placement = ".\\"; | 
|---|
| 1042 |  | 
|---|
| 1043 | VCConfiguration &conf = vcProject.Configuration; | 
|---|
| 1044 | if (conf.CompilerVersion >= NET2010) { | 
|---|
| 1045 | // adjust compiler tool defaults for VS 2010 and above | 
|---|
| 1046 | conf.compiler.Optimization = optimizeDisabled; | 
|---|
| 1047 | } | 
|---|
| 1048 | conf.compiler.AssemblerListingLocation = placement ; | 
|---|
| 1049 | conf.compiler.ObjectFile = placement ; | 
|---|
| 1050 | conf.compiler.ExceptionHandling = ehNone; | 
|---|
| 1051 | // PCH | 
|---|
| 1052 | if (usePCH) { | 
|---|
| 1053 | conf.compiler.UsePrecompiledHeader     = pchUseUsingSpecific; | 
|---|
| 1054 | conf.compiler.PrecompiledHeaderFile    = "$(IntDir)\\"+ precompPch; | 
|---|
| 1055 | conf.compiler.PrecompiledHeaderThrough = project->first( "PRECOMPILED_HEADER").toQString(); | 
|---|
| 1056 | conf.compiler.ForcedIncludeFiles       = project->values( "PRECOMPILED_HEADER").toQStringList(); | 
|---|
| 1057 | } | 
|---|
| 1058 |  | 
|---|
| 1059 | conf.compiler.parseOptions(project->values( "QMAKE_CXXFLAGS")); | 
|---|
| 1060 |  | 
|---|
| 1061 | if (project->isActiveConfig( "windows")) | 
|---|
| 1062 | conf.compiler.PreprocessorDefinitions += "_WINDOWS"; | 
|---|
| 1063 | else if (project->isActiveConfig( "console")) | 
|---|
| 1064 | conf.compiler.PreprocessorDefinitions += "_CONSOLE"; | 
|---|
| 1065 |  | 
|---|
| 1066 | conf.compiler.PreprocessorDefinitions += project->values( "DEFINES").toQStringList(); | 
|---|
| 1067 | conf.compiler.PreprocessorDefinitions += project->values( "PRL_EXPORT_DEFINES").toQStringList(); | 
|---|
| 1068 | conf.compiler.parseOptions(project->values( "MSVCPROJ_INCPATH")); | 
|---|
| 1069 | } | 
|---|
| 1070 |  | 
|---|
| 1071 | void VcprojGenerator::initLibrarianTool() | 
|---|
| 1072 | { | 
|---|
| 1073 | VCConfiguration &conf = vcProject.Configuration; | 
|---|
| 1074 | conf.librarian.OutputFile = "$(OutDir)\\"; | 
|---|
| 1075 | conf.librarian.OutputFile += project->first( "MSVCPROJ_TARGET").toQString(); | 
|---|
| 1076 | conf.librarian.AdditionalOptions += project->values( "QMAKE_LIBFLAGS").toQStringList(); | 
|---|
| 1077 | } | 
|---|
| 1078 |  | 
|---|
| 1079 | void VcprojGenerator::initManifestTool() | 
|---|
| 1080 | { | 
|---|
| 1081 | VCManifestTool &tool = vcProject.Configuration.manifestTool; | 
|---|
| 1082 | const ProString tmplt = project->first( "TEMPLATE"); | 
|---|
| 1083 | if ((tmplt == "vclib" | 
|---|
| 1084 | && !project->isActiveConfig( "embed_manifest_dll") | 
|---|
| 1085 | && !project->isActiveConfig( "static")) | 
|---|
| 1086 | || (tmplt == "vcapp" | 
|---|
| 1087 | && !project->isActiveConfig( "embed_manifest_exe"))) { | 
|---|
| 1088 | tool.EmbedManifest = _False; | 
|---|
| 1089 | } | 
|---|
| 1090 | } | 
|---|
| 1091 |  | 
|---|
| 1092 | void VcprojGenerator::initLinkerTool() | 
|---|
| 1093 | { | 
|---|
| 1094 | VCConfiguration &conf = vcProject.Configuration; | 
|---|
| 1095 | conf.linker.parseOptions(project->values( "QMAKE_LFLAGS")); | 
|---|
| 1096 |  | 
|---|
| 1097 | if (!project->values( "DEF_FILE").isEmpty()) | 
|---|
| 1098 | conf.linker.ModuleDefinitionFile = project->first( "DEF_FILE").toQString(); | 
|---|
| 1099 |  | 
|---|
| 1100 | static const char * const lflags[] = { "LIBS", "LIBS_PRIVATE", | 
|---|
| 1101 | "QMAKE_LIBS", "QMAKE_LIBS_PRIVATE", nullptr }; | 
|---|
| 1102 | for (int i = 0; lflags[i]; i++) { | 
|---|
| 1103 | const auto libs = fixLibFlags(lflags[i]); | 
|---|
| 1104 | for (const ProString &lib : libs) { | 
|---|
| 1105 | if (lib.startsWith( "/LIBPATH:")) | 
|---|
| 1106 | conf.linker.AdditionalLibraryDirectories << lib.mid(9).toQString(); | 
|---|
| 1107 | else | 
|---|
| 1108 | conf.linker.AdditionalDependencies << lib.toQString(); | 
|---|
| 1109 | } | 
|---|
| 1110 | } | 
|---|
| 1111 |  | 
|---|
| 1112 | conf.linker.OutputFile = "$(OutDir)\\"; | 
|---|
| 1113 | conf.linker.OutputFile += project->first( "MSVCPROJ_TARGET").toQString(); | 
|---|
| 1114 | } | 
|---|
| 1115 |  | 
|---|
| 1116 | void VcprojGenerator::initResourceTool() | 
|---|
| 1117 | { | 
|---|
| 1118 | VCConfiguration &conf = vcProject.Configuration; | 
|---|
| 1119 |  | 
|---|
| 1120 | ProStringList rcDefines = project->values( "RC_DEFINES"); | 
|---|
| 1121 | if (rcDefines.size() > 0) | 
|---|
| 1122 | conf.resource.PreprocessorDefinitions = rcDefines.toQStringList(); | 
|---|
| 1123 | else | 
|---|
| 1124 | conf.resource.PreprocessorDefinitions = conf.compiler.PreprocessorDefinitions; | 
|---|
| 1125 |  | 
|---|
| 1126 | for (const ProString &path : project->values( "RC_INCLUDEPATH")) { | 
|---|
| 1127 | QString fixedPath = fileFixify(path.toQString()); | 
|---|
| 1128 | if (fileInfo(fixedPath).isRelative()) { | 
|---|
| 1129 | if (fixedPath == QLatin1String( ".")) | 
|---|
| 1130 | fixedPath = QStringLiteral( "$(ProjectDir)"); | 
|---|
| 1131 | else | 
|---|
| 1132 | fixedPath.prepend(QStringLiteral( "$(ProjectDir)\\")); | 
|---|
| 1133 | } | 
|---|
| 1134 | conf.resource.AdditionalIncludeDirectories << escapeFilePath(fixedPath); | 
|---|
| 1135 | } | 
|---|
| 1136 |  | 
|---|
| 1137 | // We need to add _DEBUG for the debug version of the project, since the normal compiler defines | 
|---|
| 1138 | // do not contain it. (The compiler defines this symbol automatically, which is wy we don't need | 
|---|
| 1139 | // to add it for the compiler) However, the resource tool does not do this. | 
|---|
| 1140 | if(project->isActiveConfig( "debug")) | 
|---|
| 1141 | conf.resource.PreprocessorDefinitions += "_DEBUG"; | 
|---|
| 1142 | if (conf.CompilerVersion < NET2010 && project->isActiveConfig( "staticlib")) | 
|---|
| 1143 | conf.resource.ResourceOutputFileName = "$(OutDir)\\$(InputName).res"; | 
|---|
| 1144 | } | 
|---|
| 1145 |  | 
|---|
| 1146 | void VcprojGenerator::initIDLTool() | 
|---|
| 1147 | { | 
|---|
| 1148 | } | 
|---|
| 1149 |  | 
|---|
| 1150 | void VcprojGenerator::initCustomBuildTool() | 
|---|
| 1151 | { | 
|---|
| 1152 | } | 
|---|
| 1153 |  | 
|---|
| 1154 | void VcprojGenerator::initPreBuildEventTools() | 
|---|
| 1155 | { | 
|---|
| 1156 | } | 
|---|
| 1157 |  | 
|---|
| 1158 | void VcprojGenerator::initPostBuildEventTools() | 
|---|
| 1159 | { | 
|---|
| 1160 | VCConfiguration &conf = vcProject.Configuration; | 
|---|
| 1161 | if (!project->values( "QMAKE_POST_LINK").isEmpty()) { | 
|---|
| 1162 | QStringList cmdline = VCToolBase::fixCommandLine(var( "QMAKE_POST_LINK")); | 
|---|
| 1163 | conf.postBuild.CommandLine = cmdline; | 
|---|
| 1164 | conf.postBuild.Description = cmdline.join(QLatin1String( "\r\n")); | 
|---|
| 1165 | conf.postBuild.ExcludedFromBuild = _False; | 
|---|
| 1166 | } | 
|---|
| 1167 | if (!project->values( "MSVCPROJ_COPY_DLL").isEmpty()) { | 
|---|
| 1168 | conf.postBuild.Description += var( "MSVCPROJ_COPY_DLL_DESC"); | 
|---|
| 1169 | conf.postBuild.CommandLine += var( "MSVCPROJ_COPY_DLL"); | 
|---|
| 1170 | conf.postBuild.ExcludedFromBuild = _False; | 
|---|
| 1171 | } | 
|---|
| 1172 | } | 
|---|
| 1173 |  | 
|---|
| 1174 | void VcprojGenerator::initDeploymentTool() | 
|---|
| 1175 | { | 
|---|
| 1176 | VCConfiguration &conf = vcProject.Configuration; | 
|---|
| 1177 | QString targetPath; | 
|---|
| 1178 | targetPath = project->values( "deploy.path").join(' '); | 
|---|
| 1179 | if (targetPath.isEmpty()) | 
|---|
| 1180 | targetPath = QString( "%CSIDL_PROGRAM_FILES%\\") + project->first( "TARGET"); | 
|---|
| 1181 | if (targetPath.endsWith( "/") || targetPath.endsWith( "\\")) | 
|---|
| 1182 | targetPath.chop(1); | 
|---|
| 1183 | conf.deployment.RemoteDirectory = targetPath; | 
|---|
| 1184 | const ProStringList dllPaths = project->values( "QMAKE_DLL_PATHS"); | 
|---|
| 1185 | // Only deploy Qt libs for shared build | 
|---|
| 1186 | if (!dllPaths.isEmpty()) { | 
|---|
| 1187 | // FIXME: This code should actually resolve the libraries from all Qt modules. | 
|---|
| 1188 | ProStringList arg = project->values( "LIBS") + project->values( "LIBS_PRIVATE") | 
|---|
| 1189 | + project->values( "QMAKE_LIBS") + project->values( "QMAKE_LIBS_PRIVATE"); | 
|---|
| 1190 | bool qpaPluginDeployed = false; | 
|---|
| 1191 | for (ProStringList::ConstIterator it = arg.constBegin(); it != arg.constEnd(); ++it) { | 
|---|
| 1192 | QString dllName = (*it).toQString(); | 
|---|
| 1193 | dllName.replace(QLatin1Char('\\'), QLatin1Char('/')); | 
|---|
| 1194 | // LIBPATH isn't relevant for deployment | 
|---|
| 1195 | if (dllName.startsWith(QLatin1String( "/LIBPATH:"))) | 
|---|
| 1196 | continue; | 
|---|
| 1197 | // We want to deploy .dlls not .libs | 
|---|
| 1198 | if (dllName.endsWith(QLatin1String( ".lib"))) | 
|---|
| 1199 | dllName.replace(dllName.length() - 3, 3, QLatin1String( "dll")); | 
|---|
| 1200 | // Use only the file name and check in Qt's install path and LIBPATHs to check for existence | 
|---|
| 1201 | dllName.remove(0, dllName.lastIndexOf(QLatin1Char('/')) + 1); | 
|---|
| 1202 | QFileInfo info; | 
|---|
| 1203 | for (const ProString &dllPath : dllPaths) { | 
|---|
| 1204 | QString absoluteDllFilePath = dllPath.toQString(); | 
|---|
| 1205 | if (!absoluteDllFilePath.endsWith(QLatin1Char('/'))) | 
|---|
| 1206 | absoluteDllFilePath += QLatin1Char('/'); | 
|---|
| 1207 | absoluteDllFilePath += dllName; | 
|---|
| 1208 | info = QFileInfo(absoluteDllFilePath); | 
|---|
| 1209 | if (info.exists()) | 
|---|
| 1210 | break; | 
|---|
| 1211 | } | 
|---|
| 1212 |  | 
|---|
| 1213 | if (!info.exists()) | 
|---|
| 1214 | continue; | 
|---|
| 1215 |  | 
|---|
| 1216 | conf.deployment.AdditionalFiles += info.fileName() | 
|---|
| 1217 | + "|"+ QDir::toNativeSeparators(info.absolutePath()) | 
|---|
| 1218 | + "|"+ targetPath | 
|---|
| 1219 | + "|0;"; | 
|---|
| 1220 | if (!qpaPluginDeployed) { | 
|---|
| 1221 | QString debugInfix; | 
|---|
| 1222 | bool foundGuid = dllName.contains(QLatin1String( "Guid")); | 
|---|
| 1223 | if (foundGuid) | 
|---|
| 1224 | debugInfix = QLatin1Char('d'); | 
|---|
| 1225 |  | 
|---|
| 1226 | if (foundGuid || dllName.contains(QLatin1String( "Gui"))) { | 
|---|
| 1227 | QFileInfo info2; | 
|---|
| 1228 | for (const ProString &dllPath : dllPaths) { | 
|---|
| 1229 | QString absoluteDllFilePath = dllPath.toQString(); | 
|---|
| 1230 | if (!absoluteDllFilePath.endsWith(QLatin1Char('/'))) | 
|---|
| 1231 | absoluteDllFilePath += QLatin1Char('/'); | 
|---|
| 1232 | absoluteDllFilePath += QLatin1String( "../plugins/platforms/qwindows") | 
|---|
| 1233 | + debugInfix + QLatin1String( ".dll"); | 
|---|
| 1234 | info2 = QFileInfo(absoluteDllFilePath); | 
|---|
| 1235 | if (info2.exists()) | 
|---|
| 1236 | break; | 
|---|
| 1237 | } | 
|---|
| 1238 | if (info2.exists()) { | 
|---|
| 1239 | conf.deployment.AdditionalFiles += QLatin1String( "qwindows") + debugInfix + QLatin1String( ".dll") | 
|---|
| 1240 | + QLatin1Char('|') + QDir::toNativeSeparators(info2.absolutePath()) | 
|---|
| 1241 | + QLatin1Char('|') + targetPath + QLatin1String( "\\platforms") | 
|---|
| 1242 | + QLatin1String( "|0;"); | 
|---|
| 1243 | qpaPluginDeployed = true; | 
|---|
| 1244 | } | 
|---|
| 1245 | } | 
|---|
| 1246 | } | 
|---|
| 1247 | } | 
|---|
| 1248 | } | 
|---|
| 1249 |  | 
|---|
| 1250 | for (const ProString &item : project->values( "INSTALLS")) { | 
|---|
| 1251 | // get item.path | 
|---|
| 1252 | QString devicePath = project->first(ProKey(item + ".path")).toQString(); | 
|---|
| 1253 | if (devicePath.isEmpty()) | 
|---|
| 1254 | devicePath = targetPath; | 
|---|
| 1255 | // check if item.path is relative (! either /,\ or %) | 
|---|
| 1256 | if (!(devicePath.at(0) == QLatin1Char('/') | 
|---|
| 1257 | || devicePath.at(0) == QLatin1Char('\\') | 
|---|
| 1258 | || devicePath.at(0) == QLatin1Char('%'))) { | 
|---|
| 1259 | // create output path | 
|---|
| 1260 | devicePath = Option::fixPathToTargetOS(targetPath + QLatin1Char('\\') + devicePath); | 
|---|
| 1261 | } | 
|---|
| 1262 | // foreach d in item.files | 
|---|
| 1263 | for (const ProString &src : project->values(ProKey(item + ".files"))) { | 
|---|
| 1264 | QString itemDevicePath = devicePath; | 
|---|
| 1265 | QString source = Option::normalizePath(src.toQString()); | 
|---|
| 1266 | QString nameFilter; | 
|---|
| 1267 | QFileInfo info(source); | 
|---|
| 1268 | QString searchPath; | 
|---|
| 1269 | if (info.isDir()) { | 
|---|
| 1270 | nameFilter = QLatin1String( "*"); | 
|---|
| 1271 | itemDevicePath += "\\"+ info.fileName(); | 
|---|
| 1272 | searchPath = info.absoluteFilePath(); | 
|---|
| 1273 | } else { | 
|---|
| 1274 | nameFilter = info.fileName(); | 
|---|
| 1275 | searchPath = info.absolutePath(); | 
|---|
| 1276 | } | 
|---|
| 1277 |  | 
|---|
| 1278 | int pathSize = searchPath.size(); | 
|---|
| 1279 | QDirIterator iterator(searchPath, QStringList() << nameFilter | 
|---|
| 1280 | , QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks | 
|---|
| 1281 | , QDirIterator::Subdirectories); | 
|---|
| 1282 | // foreach dirIterator-entry in d | 
|---|
| 1283 | while(iterator.hasNext()) { | 
|---|
| 1284 | iterator.next(); | 
|---|
| 1285 |  | 
|---|
| 1286 | QString absoluteItemPath = Option::fixPathToTargetOS(QFileInfo(iterator.filePath()).absolutePath()); | 
|---|
| 1287 | // Identify if it is just another subdir | 
|---|
| 1288 | int diffSize = absoluteItemPath.size() - pathSize; | 
|---|
| 1289 | // write out rules | 
|---|
| 1290 | conf.deployment.AdditionalFiles += iterator.fileName() | 
|---|
| 1291 | + "|"+ absoluteItemPath | 
|---|
| 1292 | + "|"+ itemDevicePath + (diffSize ? (absoluteItemPath.right(diffSize)) : QLatin1String( "")) | 
|---|
| 1293 | + "|0;"; | 
|---|
| 1294 | } | 
|---|
| 1295 | } | 
|---|
| 1296 | } | 
|---|
| 1297 | } | 
|---|
| 1298 |  | 
|---|
| 1299 | void VcprojGenerator::initWinDeployQtTool() | 
|---|
| 1300 | { | 
|---|
| 1301 | VCConfiguration &conf = vcProject.Configuration; | 
|---|
| 1302 | conf.windeployqt.ExcludedFromBuild = true; | 
|---|
| 1303 | if (project->isActiveConfig( "windeployqt")) { | 
|---|
| 1304 | conf.windeployqt.Record = QStringLiteral( "$(TargetName).windeployqt.$(Platform).$(Configuration)"); | 
|---|
| 1305 | const QString commandLine = MakefileGenerator::shellQuote(QDir::toNativeSeparators(project->first( "QMAKE_WINDEPLOYQT").toQString())) | 
|---|
| 1306 | + QLatin1Char(' ') + project->values( "WINDEPLOYQT_OPTIONS").join(QLatin1Char(' ')); | 
|---|
| 1307 |  | 
|---|
| 1308 | //  Visual Studio copies all files to be deployed into the MSIL directory | 
|---|
| 1309 | //  and then invokes MDILXapCompile on it, which checks for managed code and | 
|---|
| 1310 | //  translates it into native code. The problem is that all entries of the | 
|---|
| 1311 | //  package will be copied into the MSIL directly, losing the subdirectory | 
|---|
| 1312 | //  structure (for instance for plugins). However, the MDILXapCompile call | 
|---|
| 1313 | //  itself contains the original subdirectories as parameters and hence the | 
|---|
| 1314 | //  call fails. | 
|---|
| 1315 | //  Hence the only way to get a build done is to recreate the directory | 
|---|
| 1316 | //  structure manually by invoking windeployqt a second time, so that | 
|---|
| 1317 | //  the MDILXapCompile call succeeds and deployment continues. | 
|---|
| 1318 | conf.windeployqt.CommandLine += commandLine | 
|---|
| 1319 | + QStringLiteral( " -list relative -dir \"$(MSBuildProjectDirectory)\" \"$(OutDir)\\$(TargetFileName)\" > ") | 
|---|
| 1320 | + MakefileGenerator::shellQuote(conf.windeployqt.Record); | 
|---|
| 1321 | conf.windeployqt.config = &vcProject.Configuration; | 
|---|
| 1322 | conf.windeployqt.ExcludedFromBuild = false; | 
|---|
| 1323 | } | 
|---|
| 1324 | } | 
|---|
| 1325 |  | 
|---|
| 1326 | void VcprojGenerator::initPreLinkEventTools() | 
|---|
| 1327 | { | 
|---|
| 1328 | VCConfiguration &conf = vcProject.Configuration; | 
|---|
| 1329 | if(!project->values( "QMAKE_PRE_LINK").isEmpty()) { | 
|---|
| 1330 | QStringList cmdline = VCToolBase::fixCommandLine(var( "QMAKE_PRE_LINK")); | 
|---|
| 1331 | conf.preLink.CommandLine = cmdline; | 
|---|
| 1332 | conf.preLink.Description = cmdline.join(QLatin1String( "\r\n")); | 
|---|
| 1333 | conf.preLink.ExcludedFromBuild = _False; | 
|---|
| 1334 | } | 
|---|
| 1335 | } | 
|---|
| 1336 |  | 
|---|
| 1337 | void VcprojGenerator::initRootFiles() | 
|---|
| 1338 | { | 
|---|
| 1339 | // Note: Root files do _not_ have any filter name, filter nor GUID! | 
|---|
| 1340 | vcProject.RootFiles.addFiles(project->values( "RC_FILE")); | 
|---|
| 1341 |  | 
|---|
| 1342 | vcProject.RootFiles.Project = this; | 
|---|
| 1343 | vcProject.RootFiles.Config = &(vcProject.Configuration); | 
|---|
| 1344 | } | 
|---|
| 1345 |  | 
|---|
| 1346 | void VcprojGenerator::initSourceFiles() | 
|---|
| 1347 | { | 
|---|
| 1348 | vcProject.SourceFiles.Name = "Source Files"; | 
|---|
| 1349 | vcProject.SourceFiles.Filter = "cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"; | 
|---|
| 1350 | vcProject.SourceFiles.Guid = _GUIDSourceFiles; | 
|---|
| 1351 |  | 
|---|
| 1352 | vcProject.SourceFiles.addFiles(project->values( "SOURCES")); | 
|---|
| 1353 |  | 
|---|
| 1354 | vcProject.SourceFiles.Project = this; | 
|---|
| 1355 | vcProject.SourceFiles.Config = &(vcProject.Configuration); | 
|---|
| 1356 | } | 
|---|
| 1357 |  | 
|---|
| 1358 | void VcprojGenerator::() | 
|---|
| 1359 | { | 
|---|
| 1360 | vcProject.HeaderFiles.Name = "Header Files"; | 
|---|
| 1361 | vcProject.HeaderFiles.Filter = "h;hpp;hxx;hm;inl;inc;xsd"; | 
|---|
| 1362 | vcProject.HeaderFiles.Guid = _GUIDHeaderFiles; | 
|---|
| 1363 |  | 
|---|
| 1364 | vcProject.HeaderFiles.addFiles(project->values( "HEADERS")); | 
|---|
| 1365 | if (usePCH) // Generated PCH cpp file | 
|---|
| 1366 | vcProject.HeaderFiles.addFile(precompH); | 
|---|
| 1367 |  | 
|---|
| 1368 | vcProject.HeaderFiles.Project = this; | 
|---|
| 1369 | vcProject.HeaderFiles.Config = &(vcProject.Configuration); | 
|---|
| 1370 | //    vcProject.HeaderFiles.CustomBuild = mocHdr; | 
|---|
| 1371 | //    addMocArguments(vcProject.HeaderFiles); | 
|---|
| 1372 | } | 
|---|
| 1373 |  | 
|---|
| 1374 | void VcprojGenerator::initGeneratedFiles() | 
|---|
| 1375 | { | 
|---|
| 1376 | vcProject.GeneratedFiles.Name = "Generated Files"; | 
|---|
| 1377 | vcProject.GeneratedFiles.Filter = "cpp;c;cxx;moc;h;def;odl;idl;res;"; | 
|---|
| 1378 | vcProject.GeneratedFiles.Guid = _GUIDGeneratedFiles; | 
|---|
| 1379 |  | 
|---|
| 1380 | // ### These cannot have CustomBuild (mocSrc)!! | 
|---|
| 1381 | vcProject.GeneratedFiles.addFiles(project->values( "GENERATED_SOURCES")); | 
|---|
| 1382 | vcProject.GeneratedFiles.addFiles(project->values( "GENERATED_FILES")); | 
|---|
| 1383 | vcProject.GeneratedFiles.addFiles(project->values( "IDLSOURCES")); | 
|---|
| 1384 | if (project->values( "RC_FILE").isEmpty()) | 
|---|
| 1385 | vcProject.GeneratedFiles.addFiles(project->values( "RES_FILE")); | 
|---|
| 1386 | if(!extraCompilerOutputs.isEmpty()) | 
|---|
| 1387 | vcProject.GeneratedFiles.addFiles(extraCompilerOutputs.keys()); | 
|---|
| 1388 |  | 
|---|
| 1389 | vcProject.GeneratedFiles.Project = this; | 
|---|
| 1390 | vcProject.GeneratedFiles.Config = &(vcProject.Configuration); | 
|---|
| 1391 | //    vcProject.GeneratedFiles.CustomBuild = mocSrc; | 
|---|
| 1392 | } | 
|---|
| 1393 |  | 
|---|
| 1394 | void VcprojGenerator::initLexYaccFiles() | 
|---|
| 1395 | { | 
|---|
| 1396 | vcProject.LexYaccFiles.Name = "Lex / Yacc Files"; | 
|---|
| 1397 | vcProject.LexYaccFiles.ParseFiles = _False; | 
|---|
| 1398 | vcProject.LexYaccFiles.Filter = "l;y"; | 
|---|
| 1399 | vcProject.LexYaccFiles.Guid = _GUIDLexYaccFiles; | 
|---|
| 1400 |  | 
|---|
| 1401 | vcProject.LexYaccFiles.addFiles(project->values( "LEXSOURCES")); | 
|---|
| 1402 | vcProject.LexYaccFiles.addFiles(project->values( "YACCSOURCES")); | 
|---|
| 1403 |  | 
|---|
| 1404 | vcProject.LexYaccFiles.Project = this; | 
|---|
| 1405 | vcProject.LexYaccFiles.Config = &(vcProject.Configuration); | 
|---|
| 1406 | } | 
|---|
| 1407 |  | 
|---|
| 1408 | void VcprojGenerator::initTranslationFiles() | 
|---|
| 1409 | { | 
|---|
| 1410 | vcProject.TranslationFiles.Name = "Translation Files"; | 
|---|
| 1411 | vcProject.TranslationFiles.ParseFiles = _False; | 
|---|
| 1412 | vcProject.TranslationFiles.Filter = "ts;xlf"; | 
|---|
| 1413 | vcProject.TranslationFiles.Guid = _GUIDTranslationFiles; | 
|---|
| 1414 |  | 
|---|
| 1415 | vcProject.TranslationFiles.addFiles(project->values( "TRANSLATIONS")); | 
|---|
| 1416 | vcProject.TranslationFiles.addFiles(project->values( "EXTRA_TRANSLATIONS")); | 
|---|
| 1417 |  | 
|---|
| 1418 | vcProject.TranslationFiles.Project = this; | 
|---|
| 1419 | vcProject.TranslationFiles.Config = &(vcProject.Configuration); | 
|---|
| 1420 | } | 
|---|
| 1421 |  | 
|---|
| 1422 | void VcprojGenerator::initFormFiles() | 
|---|
| 1423 | { | 
|---|
| 1424 | vcProject.FormFiles.Name = "Form Files"; | 
|---|
| 1425 | vcProject.FormFiles.ParseFiles = _False; | 
|---|
| 1426 | vcProject.FormFiles.Filter = "ui"; | 
|---|
| 1427 | vcProject.FormFiles.Guid = _GUIDFormFiles; | 
|---|
| 1428 | vcProject.FormFiles.addFiles(project->values( "FORMS")); | 
|---|
| 1429 | vcProject.FormFiles.Project = this; | 
|---|
| 1430 | vcProject.FormFiles.Config = &(vcProject.Configuration); | 
|---|
| 1431 | } | 
|---|
| 1432 |  | 
|---|
| 1433 | void VcprojGenerator::initResourceFiles() | 
|---|
| 1434 | { | 
|---|
| 1435 | vcProject.ResourceFiles.Name = "Resource Files"; | 
|---|
| 1436 | vcProject.ResourceFiles.ParseFiles = _False; | 
|---|
| 1437 | vcProject.ResourceFiles.Filter = "qrc;*"; //"rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;ts;xlf;qrc"; | 
|---|
| 1438 | vcProject.ResourceFiles.Guid = _GUIDResourceFiles; | 
|---|
| 1439 |  | 
|---|
| 1440 | // Bad hack, please look away ------------------------------------- | 
|---|
| 1441 | QString rcc_dep_cmd = project->values( "rcc.depend_command").join(' '); | 
|---|
| 1442 | if(!rcc_dep_cmd.isEmpty()) { | 
|---|
| 1443 | const QStringList qrc_files = project->values( "RESOURCES").toQStringList(); | 
|---|
| 1444 | QStringList deps; | 
|---|
| 1445 | for (const QString &qrc_file : qrc_files) { | 
|---|
| 1446 | callExtraCompilerDependCommand( "rcc", | 
|---|
| 1447 | rcc_dep_cmd, | 
|---|
| 1448 | qrc_file, | 
|---|
| 1449 | QString(), | 
|---|
| 1450 | true,  // dep_lines | 
|---|
| 1451 | &deps, | 
|---|
| 1452 | false, // existingDepsOnly | 
|---|
| 1453 | true   // checkCommandavailability | 
|---|
| 1454 | ); | 
|---|
| 1455 | } | 
|---|
| 1456 | vcProject.ResourceFiles.addFiles(deps); | 
|---|
| 1457 | } | 
|---|
| 1458 | // You may look again -------------------------------------------- | 
|---|
| 1459 |  | 
|---|
| 1460 | vcProject.ResourceFiles.addFiles(project->values( "RESOURCES")); | 
|---|
| 1461 |  | 
|---|
| 1462 | vcProject.ResourceFiles.Project = this; | 
|---|
| 1463 | vcProject.ResourceFiles.Config = &(vcProject.Configuration); | 
|---|
| 1464 | } | 
|---|
| 1465 |  | 
|---|
| 1466 | void VcprojGenerator::initDistributionFiles() | 
|---|
| 1467 | { | 
|---|
| 1468 | vcProject.DistributionFiles.Name = "Distribution Files"; | 
|---|
| 1469 | vcProject.DistributionFiles.ParseFiles = _False; | 
|---|
| 1470 | vcProject.DistributionFiles.Filter = "*"; | 
|---|
| 1471 | vcProject.DistributionFiles.Guid = _GUIDDistributionFiles; | 
|---|
| 1472 | vcProject.DistributionFiles.addFiles(project->values( "DISTFILES")); | 
|---|
| 1473 | vcProject.DistributionFiles.Project = this; | 
|---|
| 1474 | vcProject.DistributionFiles.Config = &(vcProject.Configuration); | 
|---|
| 1475 | } | 
|---|
| 1476 |  | 
|---|
| 1477 | QString VcprojGenerator::(const ProString &, | 
|---|
| 1478 | const QStringList &inputs, | 
|---|
| 1479 | const QStringList &outputs) | 
|---|
| 1480 | { | 
|---|
| 1481 | QString name = project->values(ProKey(extraCompiler + ".name")).join(' '); | 
|---|
| 1482 | if (name.isEmpty()) | 
|---|
| 1483 | name = extraCompiler.toQString(); | 
|---|
| 1484 | else | 
|---|
| 1485 | name = replaceExtraCompilerVariables(name, inputs, outputs, NoShell); | 
|---|
| 1486 | return name; | 
|---|
| 1487 | } | 
|---|
| 1488 |  | 
|---|
| 1489 | void VcprojGenerator::() | 
|---|
| 1490 | { | 
|---|
| 1491 | ProStringList otherFilters; | 
|---|
| 1492 | otherFilters << "FORMS" | 
|---|
| 1493 | << "GENERATED_FILES" | 
|---|
| 1494 | << "GENERATED_SOURCES" | 
|---|
| 1495 | << "HEADERS" | 
|---|
| 1496 | << "IDLSOURCES" | 
|---|
| 1497 | << "LEXSOURCES" | 
|---|
| 1498 | << "RC_FILE" | 
|---|
| 1499 | << "RESOURCES" | 
|---|
| 1500 | << "RES_FILE" | 
|---|
| 1501 | << "SOURCES" | 
|---|
| 1502 | << "TRANSLATIONS" | 
|---|
| 1503 | << "YACCSOURCES"; | 
|---|
| 1504 | const ProStringList &quc = project->values( "QMAKE_EXTRA_COMPILERS"); | 
|---|
| 1505 | for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { | 
|---|
| 1506 | const ProStringList &inputVars = project->values(ProKey(*it + ".input")); | 
|---|
| 1507 | ProStringList inputFiles; | 
|---|
| 1508 | for (auto var : inputVars) | 
|---|
| 1509 | inputFiles.append(project->values(var.toKey())); | 
|---|
| 1510 | const ProStringList &outputs = project->values(ProKey(*it + ".output")); | 
|---|
| 1511 |  | 
|---|
| 1512 | // Create an extra compiler filter and add the files | 
|---|
| 1513 | VCFilter ; | 
|---|
| 1514 | extraCompile.Name = extraCompilerName(it->toQString(), inputFiles.toQStringList(), | 
|---|
| 1515 | outputs.toQStringList()); | 
|---|
| 1516 | extraCompile.ParseFiles = _False; | 
|---|
| 1517 | extraCompile.Filter = ""; | 
|---|
| 1518 | extraCompile.Guid = QString(_GUIDExtraCompilerFiles) + "-"+ (*it); | 
|---|
| 1519 |  | 
|---|
| 1520 | bool addOnInput = hasBuiltinCompiler(firstExpandedOutputFileName(*it)); | 
|---|
| 1521 | if (!addOnInput) { | 
|---|
| 1522 | // If the extra compiler has a variable_out set that is already handled | 
|---|
| 1523 | // some other place, ignore it. | 
|---|
| 1524 | const ProString &outputVar = project->first(ProKey(*it + ".variable_out")); | 
|---|
| 1525 | if (!outputVar.isEmpty() && otherFilters.contains(outputVar)) | 
|---|
| 1526 | continue; | 
|---|
| 1527 |  | 
|---|
| 1528 | QString tmp_out; | 
|---|
| 1529 | if (!outputs.isEmpty()) | 
|---|
| 1530 | tmp_out = project->first(outputs.first().toKey()).toQString(); | 
|---|
| 1531 | if (project->values(ProKey(*it + ".CONFIG")).indexOf( "combine") != -1) { | 
|---|
| 1532 | // Combined output, only one file result | 
|---|
| 1533 | extraCompile.addFile(Option::fixPathToTargetOS( | 
|---|
| 1534 | replaceExtraCompilerVariables(tmp_out, QString(), QString(), NoShell), false)); | 
|---|
| 1535 | } else if (!inputVars.isEmpty()) { | 
|---|
| 1536 | // One output file per input | 
|---|
| 1537 | const ProStringList &tmp_in = project->values(inputVars.first().toKey()); | 
|---|
| 1538 | for (int i = 0; i < tmp_in.count(); ++i) { | 
|---|
| 1539 | const QString &filename = tmp_in.at(i).toQString(); | 
|---|
| 1540 | if (extraCompilerSources.contains(filename) && !otherFiltersContain(filename)) | 
|---|
| 1541 | extraCompile.addFile(Option::fixPathToTargetOS( | 
|---|
| 1542 | replaceExtraCompilerVariables(filename, tmp_out, QString(), NoShell), false)); | 
|---|
| 1543 | } | 
|---|
| 1544 | } | 
|---|
| 1545 | } else { | 
|---|
| 1546 | // In this case we the outputs have a built-in compiler, so we cannot add the custom | 
|---|
| 1547 | // build steps there. So, we turn it around and add it to the input files instead, | 
|---|
| 1548 | // provided that the input file variable is not handled already (those in otherFilters | 
|---|
| 1549 | // are handled, so we avoid them). | 
|---|
| 1550 | for (const ProString &inputVar : inputVars) { | 
|---|
| 1551 | if (!otherFilters.contains(inputVar)) { | 
|---|
| 1552 | const ProStringList &tmp_in = project->values(inputVar.toKey()); | 
|---|
| 1553 | for (int i = 0; i < tmp_in.count(); ++i) { | 
|---|
| 1554 | const QString &filename = tmp_in.at(i).toQString(); | 
|---|
| 1555 | if (extraCompilerSources.contains(filename) && !otherFiltersContain(filename)) | 
|---|
| 1556 | extraCompile.addFile(Option::fixPathToTargetOS( | 
|---|
| 1557 | replaceExtraCompilerVariables(filename, QString(), QString(), NoShell), false)); | 
|---|
| 1558 | } | 
|---|
| 1559 | } | 
|---|
| 1560 | } | 
|---|
| 1561 | } | 
|---|
| 1562 | extraCompile.Project = this; | 
|---|
| 1563 | extraCompile.Config = &(vcProject.Configuration); | 
|---|
| 1564 |  | 
|---|
| 1565 | vcProject.ExtraCompilersFiles.append(extraCompile); | 
|---|
| 1566 | } | 
|---|
| 1567 | } | 
|---|
| 1568 |  | 
|---|
| 1569 | bool VcprojGenerator::otherFiltersContain(const QString &fileName) const | 
|---|
| 1570 | { | 
|---|
| 1571 | auto filterFileMatches = [&fileName] (const VCFilterFile &ff) | 
|---|
| 1572 | { | 
|---|
| 1573 | return ff.file == fileName; | 
|---|
| 1574 | }; | 
|---|
| 1575 | for (const VCFilter *filter : { &vcProject.RootFiles, | 
|---|
| 1576 | &vcProject.SourceFiles, | 
|---|
| 1577 | &vcProject.HeaderFiles, | 
|---|
| 1578 | &vcProject.GeneratedFiles, | 
|---|
| 1579 | &vcProject.LexYaccFiles, | 
|---|
| 1580 | &vcProject.TranslationFiles, | 
|---|
| 1581 | &vcProject.FormFiles, | 
|---|
| 1582 | &vcProject.ResourceFiles, | 
|---|
| 1583 | &vcProject.DeploymentFiles, | 
|---|
| 1584 | &vcProject.DistributionFiles}) { | 
|---|
| 1585 | if (std::any_of(filter->Files.cbegin(), filter->Files.cend(), filterFileMatches)) | 
|---|
| 1586 | return true; | 
|---|
| 1587 | } | 
|---|
| 1588 | return false; | 
|---|
| 1589 | } | 
|---|
| 1590 |  | 
|---|
| 1591 | // ------------------------------------------------------------------------------------------------ | 
|---|
| 1592 | // ------------------------------------------------------------------------------------------------ | 
|---|
| 1593 |  | 
|---|
| 1594 | VCProjectWriter *VcprojGenerator::createProjectWriter() | 
|---|
| 1595 | { | 
|---|
| 1596 | return new VCProjectWriter; | 
|---|
| 1597 | } | 
|---|
| 1598 |  | 
|---|
| 1599 | QString VcprojGenerator::( | 
|---|
| 1600 | const QString &var, const QStringList &in, const QStringList &out, ReplaceFor forShell) | 
|---|
| 1601 | { | 
|---|
| 1602 | QString ret = MakefileGenerator::replaceExtraCompilerVariables(var, in, out, forShell); | 
|---|
| 1603 |  | 
|---|
| 1604 | ProStringList &defines = project->values( "VCPROJ_MAKEFILE_DEFINES"); | 
|---|
| 1605 | if(defines.isEmpty()) | 
|---|
| 1606 | defines.append(varGlue( "PRL_EXPORT_DEFINES", " -D", " -D", "") + | 
|---|
| 1607 | varGlue( "DEFINES", " -D", " -D", "")); | 
|---|
| 1608 | ret.replace(QLatin1String( "$(DEFINES)"), defines.first().toQString()); | 
|---|
| 1609 |  | 
|---|
| 1610 | ProStringList &incpath = project->values( "VCPROJ_MAKEFILE_INCPATH"); | 
|---|
| 1611 | if(incpath.isEmpty() && !this->var( "MSVCPROJ_INCPATH").isEmpty()) | 
|---|
| 1612 | incpath.append(this->var( "MSVCPROJ_INCPATH")); | 
|---|
| 1613 | ret.replace(QLatin1String( "$(INCPATH)"), incpath.join(' ')); | 
|---|
| 1614 |  | 
|---|
| 1615 | return ret; | 
|---|
| 1616 | } | 
|---|
| 1617 |  | 
|---|
| 1618 | bool VcprojGenerator::openOutput(QFile &file, const QString &/*build*/) const | 
|---|
| 1619 | { | 
|---|
| 1620 | ProString fileName = file.fileName(); | 
|---|
| 1621 | ProString extension = project->first( "TEMPLATE") == "vcsubdirs" | 
|---|
| 1622 | ? project->first( "VCSOLUTION_EXTENSION") : project->first( "VCPROJ_EXTENSION"); | 
|---|
| 1623 | if (!fileName.endsWith(extension)) { | 
|---|
| 1624 | if (fileName.isEmpty()) { | 
|---|
| 1625 | fileName = !project->first( "MAKEFILE").isEmpty() | 
|---|
| 1626 | ? project->first( "MAKEFILE") : project->first( "TARGET"); | 
|---|
| 1627 | } | 
|---|
| 1628 | file.setFileName(fileName + extension); | 
|---|
| 1629 | } | 
|---|
| 1630 | return Win32MakefileGenerator::openOutput(file, QString()); | 
|---|
| 1631 | } | 
|---|
| 1632 |  | 
|---|
| 1633 | QT_END_NAMESPACE | 
|---|
| 1634 |  | 
|---|