A typical version header now looks like this:
#ifndef VERSION_H
#define VERSION_H
namespace Version
{
const int MAJOR = 1;
const int MINOR = 6;
const int LOCAL_REVISION = 46;
const int BUILD = 16130;
inline const char* versionString()
{
return "1.6.46.16130";
}
inline const char* globalRevision()
{
return "81a3662fbc71";
}
}
#endif // VERSION_H
The code that produces the header looks like this (apologies for the formatting - the online source code formatter isn't working today):
#include <iostream>
#include <QProcess>
#include <QStringList>
#include <QFile>
#include <QTextStream>
#include <QDate>
#include <QTime>
#include <QFileInfo>
#include <QTemporaryFile>
#include <QtGlobal>
#include <cstdlib>
namespace
{
int getBuildNumber()
{
const QDate today(QDate::currentDate());
return ((today.year() - 1994) * 1000) + today.dayOfYear();
}
int getLocalHgRevision()
{
int revision = 0;
QProcess process;
process.start("hg", QStringList() << "id" << "-n");
if (process.waitForStarted() && process.waitForReadyRead())
{
revision = atoi(process.readAll().constData());
process.waitForFinished();
}
return revision;
}
QString getGlobalHgRevision()
{
QString revision = "";
QProcess process;
process.start("hg", QStringList() << "id" << "-i");
if (process.waitForStarted() && process.waitForReadyRead())
{
revision = QString(process.readAll().constData()).trimmed();
revision.remove('+');
process.waitForFinished();
}
return revision;
}
QByteArray readFile(const QString& fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly))
{
return QByteArray();
}
return file.readAll();
}
int writeFile(const QString& fileName, const int major, const int minor, const int localRevision,
const int buildNumber, const QString& globalRevision)
{
// Create a temp file containing the version info and
// only replace the existing one if they are different
QTemporaryFile tempFile;
if (tempFile.open())
{
QTextStream out(&tempFile);
out << "#ifndef VERSION_H\r\n";
out << "#define VERSION_H\r\n\r\n";
out << "namespace Version\r\n";
out << "{\r\n";
out << "\tconst int MAJOR = " << major << ";\r\n";
out << "\tconst int MINOR = " << minor << ";\r\n";
out << "\tconst int LOCAL_REVISION = " << localRevision << ";\r\n";
out << "\tconst int BUILD = " << buildNumber << ";\r\n\r\n";
out << "\tinline const char* versionString()\r\n";
out << "\t{\r\n";
out << "\t\treturn \"" << major << '.' << minor << '.' << localRevision << '.' << buildNumber << "\";\r\n";
out << "\t}\r\n\r\n";
out << "\tinline const char* globalRevision()\r\n";
out << "\t{\r\n";
out << "\t\treturn \"" << globalRevision << "\";\r\n";
out << "\t}\r\n";
out << "}\r\n\r\n";
out << "#endif // VERSION_H\r\n";
const QString tempFileName = tempFile.fileName();
tempFile.close();
if (!QFile::exists(fileName) || readFile(fileName) != readFile(tempFileName))
{
QFile::remove(fileName);
QFile::copy(tempFileName, fileName);
}
return 0;
}
else
{
std::cout << "Error creating temporary file!" << std::endl;
return 1;
}
}
}
int main(int argc, char *argv[])
{
if (argc != 4)
{
std::cout << "Usage: version major minor filename" << std::endl;
return 1;
}
const int major = atoi(argv[1]);
const int minor = atoi(argv[2]);
const int localRevision = getLocalHgRevision();
const QString globalRevision = getGlobalHgRevision();
const int buildNumber = getBuildNumber();
std::cout << major << '.' << minor << '.' << localRevision << '.' << buildNumber;
std::cout << ' ' << qPrintable(globalRevision) << std::endl;
return writeFile(argv[3], major, minor, localRevision, buildNumber, globalRevision);
}
If using CMake it's very easy to achieve this with configure_file.
ReplyDeleteJust FYI.
It would also be relatively easy using shell scripts and exec calls in the .pro file too, but I'm a bit of a sadist. :)
ReplyDeleteHey thanks for this post. I really wanted this scripts so thanks again.
ReplyDelete