📄 msvc_vcproj.cpp
字号:
/****************************************************************************** $Id: qt/msvc_vcproj.cpp 3.3.4 edited Jan 12 12:51 $**** Implementation of VcprojGenerator class.**** Copyright (C) 1992-2003 Trolltech AS. All rights reserved.**** This file is part of qmake.**** This file may be distributed under the terms of the Q Public License** as defined by Trolltech AS of Norway and appearing in the file** LICENSE.QPL included in the packaging of this file.**** This file may be distributed and/or modified under the terms of the** GNU General Public License version 2 as published by the Free Software** Foundation and appearing in the file LICENSE.GPL included in the** packaging of this file.**** Licensees holding valid Qt Enterprise Edition licenses may use this** file in accordance with the Qt Commercial License Agreement provided** with the Software.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.**** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for** information about Qt Commercial License Agreements.** See http://www.trolltech.com/qpl/ for QPL licensing information.** See http://www.trolltech.com/gpl/ for GPL licensing information.**** Contact info@trolltech.com if any conditions of this licensing are** not clear to you.************************************************************************/#include "msvc_vcproj.h"#include "option.h"#include "qtmd5.h" // SG's MD5 addon#include <qdir.h>#include <qregexp.h>#include <qdict.h>#include <quuid.h>#include <stdlib.h>#include <qsettings.h>//#define DEBUG_SOLUTION_GEN//#define DEBUG_PROJECT_GEN// Registry keys for .NET version detection -------------------------const char* _regNet2002 = "Microsoft\\VisualStudio\\7.0\\Setup\\VC\\ProductDir";const char* _regNet2003 = "Microsoft\\VisualStudio\\7.1\\Setup\\VC\\ProductDir";bool use_net2003_version(){#ifndef Q_OS_WIN32 return FALSE; // Always generate 7.0 versions on other platforms#else // Only search for the version once static int current_version = -1; if (current_version!=-1) return (current_version==71); // Fallback to .NET 2002 current_version = 70; // Get registry entries for both versions bool ok = FALSE; QSettings setting; QString path2002 = setting.readEntry(_regNet2002); QString path2003 = setting.readEntry(_regNet2003); if ( path2002.isNull() || path2003.isNull() ) { // Only have one MSVC, so use that one current_version = (path2003.isNull() ? 70 : 71); } else { // Have both, so figure out the current QString paths = getenv("PATH"); QStringList pathlist = QStringList::split(";", paths.lower()); path2003 = path2003.lower(); QStringList::iterator it; for(it=pathlist.begin(); it!=pathlist.end(); ++it) { if ((*it).contains(path2003)) { current_version = 71; } else if ((*it).contains(path2002) && current_version == 71) { fprintf( stderr, "Both .NET 2002 & .NET 2003 directories for VC found in you PATH variable!\nFallback to .NET 2002 project generation" ); current_version = 70; break; } } } return (current_version==71);#endif};// Flatfile Tags ----------------------------------------------------const char* _slnHeader70 = "Microsoft Visual Studio Solution File, Format Version 7.00";const char* _slnHeader71 = "Microsoft Visual Studio Solution File, Format Version 8.00"; // The following UUID _may_ change for later servicepacks... // If so we need to search through the registry at // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.0\Projects // to find the subkey that contains a "PossibleProjectExtension" // containing "vcproj"... // Use the hardcoded value for now so projects generated on other // platforms are actually usable.const char* _slnMSVCvcprojGUID = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}";const char* _slnProjectBeg = "\nProject(\"";const char* _slnProjectMid = "\") = ";const char* _slnProjectEnd = "\nEndProject";const char* _slnGlobalBeg = "\nGlobal";const char* _slnGlobalEnd = "\nEndGlobal";const char* _slnSolutionConf = "\n\tGlobalSection(SolutionConfiguration) = preSolution" "\n\t\tConfigName.0 = Debug" "\n\t\tConfigName.1 = Release" "\n\tEndGlobalSection";const char* _slnProjDepBeg = "\n\tGlobalSection(ProjectDependencies) = postSolution";const char* _slnProjDepEnd = "\n\tEndGlobalSection";const char* _slnProjConfBeg = "\n\tGlobalSection(ProjectConfiguration) = postSolution";const char* _slnProjRelConfTag1 = ".Release.ActiveCfg = Release|Win32";const char* _slnProjRelConfTag2 = ".Release.Build.0 = Release|Win32";const char* _slnProjDbgConfTag1 = ".Debug.ActiveCfg = Debug|Win32";const char* _slnProjDbgConfTag2 = ".Debug.Build.0 = Debug|Win32";const char* _slnProjConfEnd = "\n\tEndGlobalSection";const char* _slnExtSections = "\n\tGlobalSection(ExtensibilityGlobals) = postSolution" "\n\tEndGlobalSection" "\n\tGlobalSection(ExtensibilityAddIns) = postSolution" "\n\tEndGlobalSection";// ------------------------------------------------------------------VcprojGenerator::VcprojGenerator(QMakeProject *p) : Win32MakefileGenerator(p), init_flag(FALSE){}/* \internal Generates a project file for the given profile. Options are either a Visual Studio projectfiles, or solutionfiles by parsing recursive projectdirectories.*/bool VcprojGenerator::writeMakefile(QTextStream &t){ // Check if all requirements are fullfilled if(!project->variables()["QMAKE_FAILED_REQUIREMENTS"].isEmpty()) { fprintf(stderr, "Project file not generated because all requirements not met:\n\t%s\n", var("QMAKE_FAILED_REQUIREMENTS").latin1()); return TRUE; } // Generate project file if(project->first("TEMPLATE") == "vcapp" || project->first("TEMPLATE") == "vclib") { debug_msg(1, "Generator: MSVC.NET: Writing project file" ); t << vcProject; return TRUE; } // Generate solution file else if(project->first("TEMPLATE") == "vcsubdirs") { debug_msg(1, "Generator: MSVC.NET: Writing solution file" ); writeSubDirs(t); return TRUE; } return FALSE;}struct VcsolutionDepend { QString uuid; QString vcprojFile, orig_target, target; ::target targetType; bool debugBuild; QStringList dependencies;};QUuid VcprojGenerator::getProjectUUID(const QString &filename){ bool validUUID = TRUE; // Read GUID from variable-space QUuid uuid = project->first("GUID"); // If none, create one based on the MD5 of absolute project path if (uuid.isNull() || !filename.isNull()) { QString abspath = filename.isNull()?project->first("QMAKE_MAKEFILE"):filename; qtMD5(abspath.utf8(), (unsigned char*)(&uuid)); validUUID = !uuid.isNull(); uuid.data4[0] = (uuid.data4[0] & 0x3F) | 0x80; // UV_DCE variant uuid.data3 = (uuid.data3 & 0x0FFF) | (QUuid::Name<<12); } // If still not valid, generate new one, and suggest adding to .pro if (uuid.isNull() || !validUUID) { uuid = QUuid::createUuid(); fprintf(stderr, "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", uuid.toString().upper().latin1()); } // Store GUID in variable-space project->values("GUID") = uuid.toString().upper(); return uuid;}QUuid VcprojGenerator::increaseUUID( const QUuid &id ){ QUuid result( id ); Q_LONG dataFirst = (result.data4[0] << 24) + (result.data4[1] << 16) + (result.data4[2] << 8) + result.data4[3]; Q_LONG dataLast = (result.data4[4] << 24) + (result.data4[5] << 16) + (result.data4[6] << 8) + result.data4[7]; if ( !(dataLast++) ) dataFirst++; result.data4[0] = uchar((dataFirst >> 24) & 0xff); result.data4[1] = uchar((dataFirst >> 16) & 0xff); result.data4[2] = uchar((dataFirst >> 8) & 0xff); result.data4[3] = uchar( dataFirst & 0xff); result.data4[4] = uchar((dataLast >> 24) & 0xff); result.data4[5] = uchar((dataLast >> 16) & 0xff); result.data4[6] = uchar((dataLast >> 8) & 0xff); result.data4[7] = uchar( dataLast & 0xff); return result;}void VcprojGenerator::writeSubDirs(QTextStream &t){ if(project->first("TEMPLATE") == "subdirs") { writeHeader(t); Win32MakefileGenerator::writeSubDirs(t); return; } t << (use_net2003_version() ? _slnHeader71 : _slnHeader70); QDict<VcsolutionDepend> solution_depends; QPtrList<VcsolutionDepend> solution_cleanup; solution_cleanup.setAutoDelete(TRUE); QStringList subdirs = project->variables()["SUBDIRS"]; QString oldpwd = QDir::currentDirPath(); for(QStringList::Iterator it = subdirs.begin(); it != subdirs.end(); ++it) { QFileInfo fi(Option::fixPathToLocalOS((*it), TRUE)); if(fi.exists()) { if(fi.isDir()) { QString profile = (*it); if(!profile.endsWith(Option::dir_sep)) profile += Option::dir_sep; profile += fi.baseName() + ".pro"; subdirs.append(profile); } else { QMakeProject tmp_proj; QString dir = fi.dirPath(), fn = fi.fileName(); if(!dir.isEmpty()) { if(!QDir::setCurrent(dir)) fprintf(stderr, "Cannot find directory: %s\n", dir.latin1()); } if(tmp_proj.read(fn, oldpwd)) { if(tmp_proj.first("TEMPLATE") == "vcsubdirs") { subdirs += fileFixify(tmp_proj.variables()["SUBDIRS"]); } else if(tmp_proj.first("TEMPLATE") == "vcapp" || tmp_proj.first("TEMPLATE") == "vclib") { // Initialize a 'fake' project to get the correct variables // and to be able to extract all the dependencies VcprojGenerator tmp_vcproj(&tmp_proj); tmp_vcproj.setNoIO(TRUE); tmp_vcproj.init(); if(Option::debug_level) { QMap<QString, QStringList> &vars = tmp_proj.variables(); for(QMap<QString, QStringList>::Iterator it = vars.begin(); it != vars.end(); ++it) { if(it.key().left(1) != "." && !it.data().isEmpty()) debug_msg(1, "%s: %s === %s", fn.latin1(), it.key().latin1(), it.data().join(" :: ").latin1()); } } // We assume project filename is [QMAKE_ORIG_TARGET].vcproj QString vcproj = fixFilename(tmp_vcproj.project->first("QMAKE_ORIG_TARGET")) + project->first("VCPROJ_EXTENSION"); // If file doesn't exsist, then maybe the users configuration // doesn't allow it to be created. Skip to next... if(!QFile::exists(QDir::currentDirPath() + Option::dir_sep + vcproj)) { warn_msg(WarnLogic, "Ignored (not found) '%s'", QString(QDir::currentDirPath() + Option::dir_sep + vcproj).latin1() ); goto nextfile; // # Dirty! } VcsolutionDepend *newDep = new VcsolutionDepend; newDep->vcprojFile = fileFixify(vcproj); newDep->orig_target = tmp_proj.first("QMAKE_ORIG_TARGET"); newDep->target = tmp_proj.first("MSVCPROJ_TARGET").section(Option::dir_sep, -1); newDep->targetType = tmp_vcproj.projectTarget; newDep->debugBuild = tmp_proj.isActiveConfig("debug"); newDep->uuid = getProjectUUID(Option::fixPathToLocalOS(QDir::currentDirPath() + QDir::separator() + vcproj)).toString().upper(); // We want to store it as the .lib name. if(newDep->target.endsWith(".dll")) newDep->target = newDep->target.left(newDep->target.length()-3) + "lib"; // All projects using Forms are dependent on uic.exe if(!tmp_proj.isEmpty("FORMS")) newDep->dependencies << "uic.exe"; // Add all unknown libs to the deps QStringList where("QMAKE_LIBS"); if(!tmp_proj.isEmpty("QMAKE_INTERNAL_PRL_LIBS")) where = tmp_proj.variables()["QMAKE_INTERNAL_PRL_LIBS"]; for(QStringList::iterator wit = where.begin(); wit != where.end(); ++wit) { QStringList &l = tmp_proj.variables()[(*wit)]; for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) { QString opt = (*it); if(!opt.startsWith("/") && // Not a switch opt != newDep->target && // Not self opt != "opengl32.lib" && // We don't care about these libs opt != "glu32.lib" && // to make depgen alittle faster opt != "kernel32.lib" && opt != "user32.lib" && opt != "gdi32.lib" && opt != "comdlg32.lib" && opt != "advapi32.lib" && opt != "shell32.lib" && opt != "ole32.lib" && opt != "oleaut32.lib" && opt != "uuid.lib" && opt != "imm32.lib" && opt != "winmm.lib" && opt != "wsock32.lib" && opt != "winspool.lib" && opt != "delayimp.lib" ) { newDep->dependencies << opt.section(Option::dir_sep, -1); } } }#ifdef DEBUG_SOLUTION_GEN qDebug( "Deps for %20s: [%s]", newDep->target.latin1(), newDep->dependencies.join(" :: " ).latin1() );#endif solution_cleanup.append(newDep); solution_depends.insert(newDep->target, newDep); t << _slnProjectBeg << _slnMSVCvcprojGUID << _slnProjectMid << "\"" << newDep->orig_target << "\", \"" << newDep->vcprojFile << "\", \"" << newDep->uuid << "\""; t << _slnProjectEnd; } }nextfile: QDir::setCurrent(oldpwd); } } } t << _slnGlobalBeg; t << _slnSolutionConf; t << _slnProjDepBeg; // Figure out dependencies for(solution_cleanup.first(); solution_cleanup.current(); solution_cleanup.next()) { if(solution_cleanup.current()->targetType == StaticLib) continue; // Shortcut, Static libs are not dep. int cnt = 0; for(QStringList::iterator dit = solution_cleanup.current()->dependencies.begin(); dit != solution_cleanup.current()->dependencies.end(); ++dit) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -