1 Star 0 Fork 0

王泓/QtBinPatcher

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
QtBinPatcher.cpp 18.85 KB
一键复制 编辑 原始数据 按行查看 历史
Yuri V. Krugloff 提交于 2015-07-05 19:17 . QtBinPatcher 2.2.0 RTM
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
/*******************************************************************************
Yuri V. Krugloff. 2013-2015. http://www.tver-soft.org
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*******************************************************************************/
#include "QtBinPatcher.hpp"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <vector>
#include <algorithm>
#include "Logger.hpp"
#include "Functions.hpp"
#include "CmdLineOptions.hpp"
#include "CmdLineParser.hpp"
#include "CmdLineChecker.hpp"
#include "QMake.hpp"
#include "Backup.hpp"
//------------------------------------------------------------------------------
using namespace std;
using namespace Functions;
//------------------------------------------------------------------------------
#define QT_PATH_MAX_LEN 450
//------------------------------------------------------------------------------
#ifdef OS_WINDOWS
// Case-insensitive comparision (only for case-independent file systems).
bool caseInsensitiveComp(const char c1, const char c2)
{
return tolower(c1) == tolower(c2);
}
#endif
//------------------------------------------------------------------------------
// String comparision. For OS Windows comparision is case-insensitive.
bool strneq(const string& s1, const string& s2)
{
#ifdef OS_WINDOWS
string _s1 = s1, _s2 = s2;
transform(_s1.begin(), _s1.end(), _s1.begin(), ::tolower);
transform(_s2.begin(), _s2.end(), _s2.begin(), ::tolower);
return _s1 != _s2;
#else
return s1 != s2;
#endif
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
string TQtBinPatcher::getStartDir() const
{
string Result = normalizeSeparators(m_ArgsMap.value(OPT_QT_DIR));
if (!Result.empty())
Result = absolutePath(Result);
return Result;
}
//------------------------------------------------------------------------------
bool TQtBinPatcher::getQtDir()
{
m_QtDir = m_QMake.qtPath();
if (m_QtDir.empty()) {
LOG_E("Can't determine path to Qt directory.\n");
return false;
}
LOG_V("Path to Qt directory: \"%s\".\n", m_QtDir.c_str());
return true;
}
//------------------------------------------------------------------------------
bool TQtBinPatcher::getNewQtDir()
{
assert(hasOnlyNormalSeparators(m_QtDir));
m_NewQtDir = normalizeSeparators(m_ArgsMap.value(OPT_NEW_DIR));
if (!m_NewQtDir.empty())
m_NewQtDir = absolutePath(m_NewQtDir);
else
m_NewQtDir = m_QtDir;
LOG_V("Path to new Qt directory: \"%s\".\n", m_NewQtDir.c_str());
if (m_NewQtDir.length() > QT_PATH_MAX_LEN) {
LOG_E("Path to new Qt directory too long (%i symbols).\n"
"Path must be not longer as %i symbols.",
m_NewQtDir.length(), QT_PATH_MAX_LEN);
return false;
}
return !m_NewQtDir.empty();
}
//------------------------------------------------------------------------------
bool TQtBinPatcher::isPatchNeeded()
{
assert(hasOnlyNormalSeparators(m_NewQtDir));
string OldQtDir = normalizeSeparators(m_QMake.qtInstallPrefix());
if (!OldQtDir.empty() && !m_NewQtDir.empty())
return strneq(OldQtDir, m_NewQtDir);
return false;
}
//------------------------------------------------------------------------------
void TQtBinPatcher::addTxtPatchValues(const string& oldPath)
{
assert(hasOnlyNormalSeparators(oldPath));
assert(hasOnlyNormalSeparators(m_NewQtDir));
if (!oldPath.empty()) {
m_TxtPatchValues[oldPath] = m_NewQtDir;
#ifdef OS_WINDOWS
string Old = oldPath;
string New = m_NewQtDir;
replace(Old.begin(), Old.end(), '/', '\\');
replace(New.begin(), New.end(), '/', '\\');
m_TxtPatchValues[Old] = New;
string NewQtDirDS = m_NewQtDir;
replace(&NewQtDirDS, '/', "\\\\");
Old = oldPath;
replace(&Old, '/', "\\\\");
m_TxtPatchValues[Old] = NewQtDirDS;
#endif
}
}
//------------------------------------------------------------------------------
void TQtBinPatcher::createBinPatchValues()
{
struct TParam {
const char* const Name;
const char* const Prefix;
};
static const TParam Params[] = {
{ "QT_INSTALL_PREFIX", "qt_prfxpath="},
{ "QT_INSTALL_ARCHDATA", "qt_adatpath="},
{ "QT_INSTALL_DOCS", "qt_docspath="},
{ "QT_INSTALL_HEADERS", "qt_hdrspath="},
{ "QT_INSTALL_LIBS", "qt_libspath="},
{ "QT_INSTALL_LIBEXECS", "qt_lbexpath="},
{ "QT_INSTALL_BINS", "qt_binspath="},
{ "QT_INSTALL_PLUGINS", "qt_plugpath="},
{ "QT_INSTALL_IMPORTS", "qt_impspath="},
{ "QT_INSTALL_QML", "qt_qml2path="},
{ "QT_INSTALL_DATA", "qt_datapath="},
{ "QT_INSTALL_TRANSLATIONS", "qt_trnspath="},
{ "QT_INSTALL_EXAMPLES", "qt_xmplpath="},
{ "QT_INSTALL_DEMOS", "qt_demopath="},
{ "QT_INSTALL_TESTS", "qt_tstspath="},
{ "QT_HOST_PREFIX", "qt_hpfxpath="},
{ "QT_INSTALL_PREFIX", "qt_epfxpath="}, // "QT_EXT_PREFIX"
{ "QT_HOST_BINS", "qt_hbinpath="},
{ "QT_HOST_DATA", "qt_hdatpath="},
{ "QT_HOST_LIBS", "qt_hlibpath="}
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string newQtDirNative = normalizeSeparators(m_NewQtDir);
for (size_t i = 0; i < sizeof(Params)/sizeof(Params[0]); ++i)
{
const TParam& Param = Params[i];
if (!m_QMake.value(Param.Name).empty()) {
string NewValue = Param.Prefix;
NewValue.append(newQtDirNative);
const std::string suffix = m_QMake.suffix(Param.Name);
if (!suffix.empty())
NewValue += separator() + suffix;
m_BinPatchValues[Param.Prefix] = NewValue;
}
else {
LOG_V("Variable \"%s\" not found in qmake output.\n", Param.Name);
}
}
}
//------------------------------------------------------------------------------
void TQtBinPatcher::createPatchValues()
{
m_TxtPatchValues.clear();
m_BinPatchValues.clear();
addTxtPatchValues(normalizeSeparators(m_QMake.qtInstallPrefix()));
createBinPatchValues();
const TStringList* pValues = m_ArgsMap.values(OPT_OLD_DIR);
if (pValues != NULL)
for (TStringList::const_iterator Iter = pValues->begin(); Iter != pValues->end(); ++Iter)
addTxtPatchValues(normalizeSeparators(*Iter));
LOG_V("\nPatch values for text files:\n%s",
stringMapToStr(m_TxtPatchValues, " \"", "\" -> \"", "\"\n").c_str());
LOG_V("\nPatch values for binary files:\n%s",
stringMapToStr(m_BinPatchValues, " \"", "\" -> \"", "\"\n").c_str());
}
//------------------------------------------------------------------------------
bool TQtBinPatcher::createTxtFilesForPatchList()
{
struct TElement {
const char* const Dir;
const char* const Name;
const bool Recursive;
};
// Files for patching in Qt4.
static const TElement Elements4[] = {
{ "/lib/", "*.prl", false },
{ "/demos/shared/", "libdemo_shared.prl", false },
{ "/lib/pkgconfig/", "Qt*.pc", false },
{ "/lib/pkgconfig/", "phonon*.pc", false },
#if defined(OS_WINDOWS)
{ "/mkspecs/default/", "qmake.conf", false },
{ "/", ".qmake.cache", false }
#elif defined(OS_LINUX)
{ "/lib/pkgconfig/", "qt*.pc", false },
{ "/lib/", "*.la", false },
{ "/mkspecs/", "qconfig.pri", false }
#endif
};
// Files for patching in Qt5.
static const TElement Elements5[] = {
{ "/", "*.la", true },
{ "/", "*.prl", true },
{ "/lib/pkgconfig/", "Qt5*.pc", true },
{ "/lib/pkgconfig/", "Enginio*.pc", true },
{ "/", "*.pri", true },
{ "/lib/cmake/Qt5LinguistTools/", "Qt5LinguistToolsConfig.cmake", false },
{ "/mkspecs/default-host/", "qmake.conf", false },
#ifdef OS_WINDOWS
{ "/mkspecs/default/", "qmake.conf", false },
{ "/", ".qmake.cache", false },
{ "/lib/", "prl.txt", false }
#endif
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
m_TxtFilesForPatch.clear();
const TElement* Elements;
size_t Count;
switch (m_QMake.qtVersion()) {
case '4' :
Elements = Elements4;
Count = sizeof(Elements4)/sizeof(Elements4[0]);
break;
case '5' :
Elements = Elements5;
Count = sizeof(Elements5)/sizeof(Elements5[0]);
break;
default :
LOG_E("Unsupported Qt version (%c).", m_QMake.qtVersion());
return false;
}
for (size_t i = 0; i < Count; ++i) {
if (Elements[i].Recursive)
splice(&m_TxtFilesForPatch, findFilesRecursive(m_QtDir + Elements[i].Dir, Elements[i].Name));
else
splice(&m_TxtFilesForPatch, findFiles(m_QtDir + Elements[i].Dir, Elements[i].Name));
}
LOG_V("\nList of text files for patch:\n%s\n",
stringListToStr(m_TxtFilesForPatch, " ", "\n").c_str());
return true;
}
//------------------------------------------------------------------------------
bool TQtBinPatcher::createBinFilesForPatchList()
{
struct TElement {
const char* const Dir;
const char* const Name;
};
// Files for patching in Qt4.
static const TElement Elements4[] = {
#if defined(OS_WINDOWS)
{ "/bin/", "qmake.exe" },
{ "/bin/", "lrelease.exe" },
{ "/bin/", "QtCore*.dll" },
{ "/lib/", "QtCore*.dll" }
#elif defined(OS_LINUX)
{ "/bin/", "qmake" },
{ "/bin/", "lrelease" },
{ "/lib/", "libQtCore.so" }
#endif
};
// Files for patching in Qt5.
static const TElement Elements5[] = {
#if defined(OS_WINDOWS)
{ "/bin/", "qmake.exe" },
{ "/bin/", "lrelease.exe" },
{ "/bin/", "qdoc.exe" },
{ "/bin/", "Qt5Core*.dll" },
{ "/lib/", "Qt5Core*.dll" }
#elif defined(OS_LINUX)
{ "/bin/", "qmake" },
{ "/bin/", "lrelease" },
{ "/bin/", "qdoc" },
{ "/lib/", "libQtCore.so" }
#endif
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
m_BinFilesForPatch.clear();
const TElement* Elements;
size_t Count;
switch (m_QMake.qtVersion()) {
case '4' :
Elements = Elements4;
Count = sizeof(Elements4)/sizeof(Elements4[0]);
break;
case '5' :
Elements = Elements5;
Count = sizeof(Elements5)/sizeof(Elements5[0]);
break;
default :
LOG_E("Unsupported Qt version (%c).", m_QMake.qtVersion());
return false;
}
for (size_t i = 0; i < Count; ++i)
splice(&m_BinFilesForPatch, findFiles(m_QtDir + Elements[i].Dir, Elements[i].Name));
LOG_V("\nList of binary files for patch:\n%s\n",
stringListToStr(m_BinFilesForPatch, " ", "\n").c_str());
return true;
}
//------------------------------------------------------------------------------
bool TQtBinPatcher::patchTxtFile(const string& fileName)
{
LOG("Patching text file \"%s\".\n", fileName.c_str());
bool Result = false;
FILE* File = fopen(fileName.c_str(), "r+b");
if (File!= NULL)
{
vector<char> Buf;
long FileLength = getFileSize(File);
if (FileLength > 0)
{
Buf.resize(FileLength);
if (fread(Buf.data(), FileLength, 1, File) == 1) // TODO: C++11 requred!
{
for (TStringMap::const_iterator Iter = m_TxtPatchValues.begin(); Iter != m_TxtPatchValues.end(); ++Iter)
{
string::size_type Delta = 0;
vector<char>::iterator Found;
while ((Found = search(Buf.begin() + Delta, Buf.end(),
Iter->first.begin(), Iter->first.end()
#ifdef OS_WINDOWS
, caseInsensitiveComp
#endif
))
!= Buf.end())
{
Delta = Found - Buf.begin() + static_cast<int>(Iter->second.length());
Found = Buf.erase(Found, Found + Iter->first.length());
Buf.insert(Found, Iter->second.begin(), Iter->second.end());
}
}
zeroFile(File);
if (fwrite(Buf.data(), Buf.size(), 1, File) == 1)
Result = true;
else
LOG_E("Error writing to file \"%s\".\n", fileName.c_str());
}
else {
LOG_E("Error reading from file \"%s\".\n", fileName.c_str());
}
}
else {
LOG_V(" File is empty. Skipping.\n");
Result = true;
}
fclose(File);
}
else {
LOG_E("Error opening file \"%s\". Error %i.\n", fileName.c_str(), errno);
}
return Result;
}
//------------------------------------------------------------------------------
bool TQtBinPatcher::patchBinFile(const string& fileName)
{
LOG("Patching binary file \"%s\".\n", fileName.c_str());
bool Result = false;
FILE* File = fopen(fileName.c_str(), "r+b");
if (File != NULL)
{
long BufSize = getFileSize(File);
char* Buf = new char[BufSize];
if (fread(Buf, BufSize, 1, File) == 1)
{
for (TStringMap::const_iterator Iter = m_BinPatchValues.begin(); Iter != m_BinPatchValues.end(); ++Iter)
{
char* First = Buf;
while ((First = search(First, Buf + BufSize,
Iter->first.begin(), Iter->first.end()))
!= Buf + BufSize)
{
strcpy(First, Iter->second.c_str());
First += Iter->second.length();
}
}
rewind(File);
if (fwrite(Buf, BufSize, 1, File) == 1)
Result = true;
else
LOG_E("Error writing to file \"%s\".\n", fileName.c_str());
}
else {
LOG_E("Error reading from file \"%s\".\n", fileName.c_str());
}
delete[] Buf;
fclose(File);
}
else {
LOG_E("Error opening file \"%s\". Error %i.\n", fileName.c_str(), errno);
}
return Result;
}
//------------------------------------------------------------------------------
bool TQtBinPatcher::patchTxtFiles()
{
for (TStringList::const_iterator Iter = m_TxtFilesForPatch.begin(); Iter != m_TxtFilesForPatch.end(); ++Iter)
if (!patchTxtFile(*Iter))
return false;
return true;
}
//------------------------------------------------------------------------------
bool TQtBinPatcher::patchBinFiles()
{
for (TStringList::const_iterator Iter = m_BinFilesForPatch.begin(); Iter != m_BinFilesForPatch.end(); ++Iter)
if (!patchBinFile(*Iter))
return false;
return true;
}
//------------------------------------------------------------------------------
bool TQtBinPatcher::exec()
{
if (!getQtDir())
return false;
if (!getNewQtDir())
return false;
TBackup Backup;
Backup.setSkipBackup(m_ArgsMap.contains(OPT_NOBACKUP));
if (!Backup.backupFile(m_QtDir + "/bin/qt.conf", TBackup::bmRename))
return false;
if (!isPatchNeeded()) {
if (m_ArgsMap.contains(OPT_FORCE)) {
LOG("\nThe new and the old pathes to Qt directory are the same.\n"
"Perform forced patching.\n\n");
}
else {
LOG("\nThe new and the old pathes to Qt directory are the same.\n"
"Patching not needed.\n");
return true;
}
}
createPatchValues();
if (!createTxtFilesForPatchList() || !createBinFilesForPatchList())
return false;
if (!Backup.backupFiles(m_TxtFilesForPatch) || !Backup.backupFiles(m_BinFilesForPatch))
return false;
if (!patchTxtFiles() || !patchBinFiles())
return false;
// Finalization.
if (m_ArgsMap.contains(OPT_BACKUP))
Backup.save();
else
if (!Backup.remove())
return false;
return true;
}
//------------------------------------------------------------------------------
TQtBinPatcher::TQtBinPatcher(const TStringListMap& argsMap)
: m_ArgsMap(argsMap),
m_QMake(getStartDir()),
m_hasError(false)
{
if (m_QMake.hasError()) {
m_hasError = true;
LOG_E("%s\n", m_QMake.errorString().c_str());
}
else {
m_hasError = !exec();
}
}
//------------------------------------------------------------------------------
bool TQtBinPatcher::exec(const TStringListMap& argsMap)
{
TQtBinPatcher QtBinPatcher(argsMap);
return !QtBinPatcher.m_hasError;
}
//------------------------------------------------------------------------------
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/kuletco/QtBinPatcher.git
git@gitee.com:kuletco/QtBinPatcher.git
kuletco
QtBinPatcher
QtBinPatcher
master

搜索帮助