qt6-bb10/tools/configure/configureapp.cpp

423 lines
15 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "configureapp.h"
#include "environment.h"
#include <qdir.h>
#include <qdiriterator.h>
#include <qtemporaryfile.h>
#include <qstandardpaths.h>
#include <qstack.h>
#include <qdebug.h>
#include <qfileinfo.h>
#include <qtextstream.h>
#include <qregexp.h>
#include <qhash.h>
#include <iostream>
#include <string>
#include <fstream>
#include <windows.h>
QT_BEGIN_NAMESPACE
std::ostream &operator<<(std::ostream &s, const QString &val) {
s << val.toLocal8Bit().data();
return s;
}
using namespace std;
Configure::Configure(int& argc, char** argv)
{
int i;
for (i = 1; i < argc; i++)
configCmdLine += argv[ i ];
if (configCmdLine.size() >= 2 && configCmdLine.at(0) == "-srcdir") {
sourcePath = QDir::cleanPath(configCmdLine.at(1));
sourceDir = QDir(sourcePath);
configCmdLine.erase(configCmdLine.begin(), configCmdLine.begin() + 2);
} else {
// Get the path to the executable
wchar_t module_name[MAX_PATH];
GetModuleFileName(0, module_name, sizeof(module_name) / sizeof(wchar_t));
QFileInfo sourcePathInfo = QString::fromWCharArray(module_name);
sourcePath = sourcePathInfo.absolutePath();
sourceDir = sourcePathInfo.dir();
}
buildPath = QDir::currentPath();
if (sourceDir != buildDir) { //shadow builds!
QDir(buildPath).mkpath("bin");
buildDir.mkpath("mkspecs");
}
if (dictionary[ "QMAKESPEC" ].size() == 0) {
dictionary[ "QMAKESPEC" ] = Environment::detectQMakeSpec();
dictionary[ "QMAKESPEC_FROM" ] = "detected";
}
dictionary[ "SYNCQT" ] = "auto";
QString tmp = dictionary[ "QMAKESPEC" ];
if (tmp.contains("\\")) {
tmp = tmp.mid(tmp.lastIndexOf("\\") + 1);
} else {
tmp = tmp.mid(tmp.lastIndexOf("/") + 1);
}
dictionary[ "QMAKESPEC" ] = tmp;
}
Configure::~Configure()
{
}
void Configure::parseCmdLine()
{
sourcePathMangled = sourcePath;
buildPathMangled = buildPath;
if (configCmdLine.size() && configCmdLine.at(0) == "-top-level") {
dictionary[ "TOPLEVEL" ] = "yes";
configCmdLine.removeAt(0);
sourcePathMangled = QFileInfo(sourcePath).path();
buildPathMangled = QFileInfo(buildPath).path();
}
qmakeCmdLine = configCmdLine;
int argCount = configCmdLine.size();
int i = 0;
// Look first for -redo
for (int k = 0 ; k < argCount; ++k) {
if (configCmdLine.at(k) == "-redo") {
configCmdLine.removeAt(k);
if (!reloadCmdLine(k)) {
dictionary["DONE"] = "error";
return;
}
argCount = configCmdLine.size();
break;
}
}
// Then look for XQMAKESPEC
bool isDeviceMkspec = false;
for (int j = 0 ; j < argCount; ++j)
{
if ((configCmdLine.at(j) == "-xplatform") || (configCmdLine.at(j) == "-device")) {
isDeviceMkspec = (configCmdLine.at(j) == "-device");
++j;
if (j == argCount)
break;
dictionary["XQMAKESPEC"] = configCmdLine.at(j);
break;
}
}
for (; i<configCmdLine.size(); ++i) {
if (configCmdLine.at(i) == "-platform") {
++i;
if (i == argCount)
break;
dictionary[ "QMAKESPEC" ] = configCmdLine.at(i);
dictionary[ "QMAKESPEC_FROM" ] = "commandline";
} else if (configCmdLine.at(i) == "-xplatform"
|| configCmdLine.at(i) == "-device") {
++i;
// do nothing
}
else if (configCmdLine.at(i) == "-no-syncqt")
dictionary[ "SYNCQT" ] = "no";
else if (configCmdLine.at(i) == "-make-tool") {
++i;
if (i == argCount)
break;
dictionary[ "MAKE" ] = configCmdLine.at(i);
}
}
// Ensure that QMAKESPEC exists in the mkspecs folder
const QString mkspecPath(sourcePath + "/mkspecs");
QDirIterator itMkspecs(mkspecPath, QDir::AllDirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
QStringList mkspecs;
while (itMkspecs.hasNext()) {
QString mkspec = itMkspecs.next();
// Remove base PATH
mkspec.remove(0, mkspecPath.length() + 1);
mkspecs << mkspec;
}
if (dictionary["QMAKESPEC"].toLower() == "features"
|| !mkspecs.contains(dictionary["QMAKESPEC"], Qt::CaseInsensitive)) {
dictionary[ "DONE" ] = "error";
if (dictionary ["QMAKESPEC_FROM"] == "commandline") {
cout << "Invalid option \"" << dictionary["QMAKESPEC"] << "\" for -platform." << endl;
} else { // was autodetected from environment
cout << "Unable to detect the platform from environment. Use -platform command line" << endl
<< "argument and run configure again." << endl;
}
cout << "See the README file for a list of supported operating systems and compilers." << endl;
} else {
if (dictionary[ "QMAKESPEC" ].endsWith("-icc") ||
dictionary[ "QMAKESPEC" ].endsWith("-msvc2012") ||
dictionary[ "QMAKESPEC" ].endsWith("-msvc2013") ||
dictionary[ "QMAKESPEC" ].endsWith("-msvc2015") ||
dictionary[ "QMAKESPEC" ].endsWith("-msvc2017")) {
if (dictionary[ "MAKE" ].isEmpty()) dictionary[ "MAKE" ] = "nmake";
dictionary[ "QMAKEMAKEFILE" ] = "Makefile.win32";
} else if (dictionary[ "QMAKESPEC" ].startsWith(QLatin1String("win32-g++"))) {
if (dictionary[ "MAKE" ].isEmpty()) dictionary[ "MAKE" ] = "mingw32-make";
dictionary[ "QMAKEMAKEFILE" ] = "Makefile.unix";
} else {
if (dictionary[ "MAKE" ].isEmpty()) dictionary[ "MAKE" ] = "make";
dictionary[ "QMAKEMAKEFILE" ] = "Makefile.win32";
}
}
if (isDeviceMkspec) {
const QStringList devices = mkspecs.filter("devices/", Qt::CaseInsensitive);
const QStringList family = devices.filter(dictionary["XQMAKESPEC"], Qt::CaseInsensitive);
if (family.isEmpty()) {
dictionary[ "DONE" ] = "error";
cout << "Error: No device matching '" << dictionary["XQMAKESPEC"] << "'." << endl;
} else if (family.size() > 1) {
dictionary[ "DONE" ] = "error";
cout << "Error: Multiple matches for device '" << dictionary["XQMAKESPEC"] << "'. Candidates are:" << endl;
foreach (const QString &device, family)
cout << "\t* " << device << endl;
} else {
Q_ASSERT(family.size() == 1);
dictionary["XQMAKESPEC"] = family.at(0);
}
} else {
// Ensure that -spec (XQMAKESPEC) exists in the mkspecs folder as well
if (dictionary.contains("XQMAKESPEC") &&
!mkspecs.contains(dictionary["XQMAKESPEC"], Qt::CaseInsensitive)) {
dictionary[ "DONE" ] = "error";
cout << "Invalid option \"" << dictionary["XQMAKESPEC"] << "\" for -xplatform." << endl;
}
}
}
void Configure::generateHeaders()
{
if (dictionary["SYNCQT"] == "auto")
dictionary["SYNCQT"] = QFile::exists(sourcePath + "/.git") ? "yes" : "no";
if (dictionary["SYNCQT"] == "yes") {
if (!QStandardPaths::findExecutable(QStringLiteral("perl.exe")).isEmpty()) {
cout << "Running syncqt..." << endl;
QStringList args;
args << "perl" << "-w";
args += sourcePath + "/bin/syncqt.pl";
args << "-version" << QT_VERSION_STR << "-minimal" << "-module" << "QtCore";
args += sourcePath;
int retc = Environment::execute(args, QStringList(), QStringList());
if (retc) {
cout << "syncqt failed, return code " << retc << endl << endl;
dictionary["DONE"] = "error";
}
} else {
cout << "Perl not found in environment - cannot run syncqt." << endl;
dictionary["DONE"] = "error";
}
}
}
void Configure::buildQmake()
{
{
QStringList args;
// Build qmake
QString pwd = QDir::currentPath();
if (!QDir(buildPath).mkpath("qmake")) {
cout << "Cannot create qmake build dir." << endl;
dictionary[ "DONE" ] = "error";
return;
}
if (!QDir::setCurrent(buildPath + "/qmake")) {
cout << "Cannot enter qmake build dir." << endl;
dictionary[ "DONE" ] = "error";
return;
}
QString makefile = "Makefile";
{
QFile out(makefile);
if (out.open(QFile::WriteOnly | QFile::Text)) {
QTextStream stream(&out);
stream << "#AutoGenerated by configure.exe" << endl
<< "BUILD_PATH = .." << endl
<< "SOURCE_PATH = " << QDir::toNativeSeparators(sourcePath) << endl
<< "INC_PATH = " << QDir::toNativeSeparators(
(QFile::exists(sourcePath + "/.git") ? ".." : sourcePath)
+ "/include") << endl;
stream << "QT_VERSION = " QT_VERSION_STR << endl
<< "QT_MAJOR_VERSION = " QT_STRINGIFY(QT_VERSION_MAJOR) << endl
<< "QT_MINOR_VERSION = " QT_STRINGIFY(QT_VERSION_MINOR) << endl
<< "QT_PATCH_VERSION = " QT_STRINGIFY(QT_VERSION_PATCH) << endl;
if (dictionary[ "QMAKESPEC" ].startsWith("win32-g++")) {
stream << "QMAKESPEC = $(SOURCE_PATH)\\mkspecs\\" << dictionary[ "QMAKESPEC" ] << endl
<< "CONFIG_CXXFLAGS = -std=c++11 -ffunction-sections" << endl
<< "CONFIG_LFLAGS = -Wl,--gc-sections" << endl;
QFile in(sourcePath + "/qmake/Makefile.unix.win32");
if (in.open(QFile::ReadOnly | QFile::Text))
stream << in.readAll();
QFile in2(sourcePath + "/qmake/Makefile.unix.mingw");
if (in2.open(QFile::ReadOnly | QFile::Text))
stream << in2.readAll();
} else {
stream << "QMAKESPEC = " << dictionary["QMAKESPEC"] << endl;
}
stream << "\n\n";
QFile in(sourcePath + "/qmake/" + dictionary["QMAKEMAKEFILE"]);
if (in.open(QFile::ReadOnly | QFile::Text)) {
QString d = in.readAll();
//### need replaces (like configure.sh)? --Sam
stream << d << endl;
}
stream.flush();
out.close();
}
}
args += dictionary[ "MAKE" ];
args += "-f";
args += makefile;
cout << "Creating qmake..." << endl;
int exitCode = Environment::execute(args, QStringList(), QStringList());
if (exitCode) {
args.clear();
args += dictionary[ "MAKE" ];
args += "-f";
args += makefile;
args += "clean";
exitCode = Environment::execute(args, QStringList(), QStringList());
if (exitCode) {
cout << "Cleaning qmake failed, return code " << exitCode << endl << endl;
dictionary[ "DONE" ] = "error";
} else {
args.clear();
args += dictionary[ "MAKE" ];
args += "-f";
args += makefile;
exitCode = Environment::execute(args, QStringList(), QStringList());
if (exitCode) {
cout << "Building qmake failed, return code " << exitCode << endl << endl;
dictionary[ "DONE" ] = "error";
}
}
}
QDir::setCurrent(pwd);
}
// Generate qt.conf
QFile confFile(buildPath + "/bin/qt.conf");
if (confFile.open(QFile::WriteOnly | QFile::Text)) { // Truncates any existing file.
QTextStream confStream(&confFile);
confStream << "[EffectivePaths]" << endl
<< "Prefix=.." << endl
<< "[Paths]" << endl
<< "TargetSpec=" << (dictionary.contains("XQMAKESPEC")
? dictionary["XQMAKESPEC"] : dictionary["QMAKESPEC"]) << endl
<< "HostSpec=" << dictionary["QMAKESPEC"] << endl;
if (sourcePath != buildPath)
confStream << "[EffectiveSourcePaths]" << endl
<< "Prefix=" << sourcePath << endl;
confStream.flush();
confFile.close();
}
}
void Configure::configure()
{
QStringList args;
args << buildPath + "/bin/qmake"
<< sourcePathMangled
<< "--" << qmakeCmdLine;
QString pwd = QDir::currentPath();
QDir::setCurrent(buildPathMangled);
if (int exitCode = Environment::execute(args, QStringList(), QStringList())) {
cout << "Qmake failed, return code " << exitCode << endl << endl;
dictionary[ "DONE" ] = "error";
}
QDir::setCurrent(pwd);
}
bool Configure::reloadCmdLine(int idx)
{
QFile inFile(buildPathMangled + "/config.opt");
if (!inFile.open(QFile::ReadOnly)) {
inFile.setFileName(buildPath + "/config.opt");
if (!inFile.open(QFile::ReadOnly)) {
inFile.setFileName(buildPath + "/configure.cache");
if (!inFile.open(QFile::ReadOnly)) {
cout << "No config.opt present - cannot redo configuration." << endl;
return false;
}
}
}
QTextStream inStream(&inFile);
while (!inStream.atEnd())
configCmdLine.insert(idx++, inStream.readLine().trimmed());
return true;
}
bool Configure::isDone()
{
return !dictionary["DONE"].isEmpty();
}
bool Configure::isOk()
{
return (dictionary[ "DONE" ] != "error");
}
QT_END_NAMESPACE