summaryrefslogtreecommitdiffstats
path: root/src/qtchooser/main.cpp
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2012-10-26 17:32:27 -0700
committerThiago Macieira <thiago.macieira@intel.com>2012-12-19 09:02:11 +0100
commit49d2e10c94c1b66de12b05eeba163c6ec8db948c (patch)
treec52b0b8437cfe0823ea6880302e491cf27957e62 /src/qtchooser/main.cpp
parenta2cfcef7a5369e2f130eaff7464aea1caed2119e (diff)
Long live the Qt tool chooser
This tool wraps the execution of the other tools. It's supposed to live on systems' /usr/bin dir or equivalent. WIP: need to compile on Mac, I need to know which libs are necessary for the FSFindFolder function (can't find the documentation on Apple's website). Change-Id: I1c429a159a4e02b78a835888d470514d8e4a23a7 Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src/qtchooser/main.cpp')
-rw-r--r--src/qtchooser/main.cpp371
1 files changed, 371 insertions, 0 deletions
diff --git a/src/qtchooser/main.cpp b/src/qtchooser/main.cpp
new file mode 100644
index 0000000..a7b3599
--- /dev/null
+++ b/src/qtchooser/main.cpp
@@ -0,0 +1,371 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Intel Corporation.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ * This tool is meant to wrap the other Qt tools by searching for different
+ * instances of Qt on the system. It's mostly destined to be used on Unix
+ * systems other than Mac OS X. For that reason, it uses POSIX APIs and does
+ * not try to be compatible with the DOS or Win32 APIs. In particular, it uses
+ * opendir(3) and readdir(3) as well as paths exclusively separated by slashes.
+ */
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#if defined(_WIN32) || defined(__WIN32__)
+# include <process.h>
+# define execv _execv
+# define PATH_SEP "\\"
+#else
+# include <sys/types.h>
+# include <dirent.h>
+# include <libgen.h>
+# include <unistd.h>
+# define PATH_SEP "/"
+#endif
+
+using namespace std;
+
+static bool testMode = false;
+static const char *argv0;
+enum Mode {
+ RunTool,
+ ListVersions,
+ PrintEnvironment
+};
+
+struct Sdk
+{
+ string name;
+ string configFile;
+ string toolsPath;
+ string librariesPath;
+
+ bool isValid() const { return !toolsPath.empty(); }
+};
+
+struct ToolWrapper
+{
+ int listVersions();
+ int printEnvironment(const string &targetSdk);
+ int runTool(const string &targetSdk, const string &targetTool, char **argv);
+
+private:
+ vector<string> searchPaths() const;
+
+ typedef bool (ToolWrapper:: *VisitFunction)(const string &targetSdk, Sdk &item);
+ Sdk iterateSdks(const string &targetSdk, VisitFunction visit);
+ Sdk selectSdk(const string &targetSdk);
+
+ bool printSdk(const string &, Sdk &sdk);
+ bool matchSdk(const string &targetSdk, Sdk &sdk);
+};
+
+int ToolWrapper::listVersions()
+{
+ iterateSdks(string(), &ToolWrapper::printSdk);
+ return 0;
+}
+
+int ToolWrapper::printEnvironment(const string &targetSdk)
+{
+ Sdk sdk = selectSdk(targetSdk);
+ if (!sdk.isValid())
+ return 1;
+
+ // ### The paths and the name are not escaped, so hopefully no one will be
+ // installing Qt on paths containing backslashes or quotes on Unix. Paths
+ // with spaces are taken into account by surrounding the arguments with
+ // quotes.
+
+ printf("QT_SELECT=\"%s\"\n", sdk.name.c_str());
+ printf("QTTOOLDIR=\"%s\"\n", sdk.toolsPath.c_str());
+ printf("QTLIBDIR=\"%s\"\n", sdk.librariesPath.c_str());
+ return 0;
+}
+
+int ToolWrapper::runTool(const string &targetSdk, const string &targetTool, char **argv)
+{
+ Sdk sdk = selectSdk(targetSdk);
+ if (!sdk.isValid())
+ return 1;
+
+ string tool = sdk.toolsPath + PATH_SEP + targetTool;
+ argv[0] = &tool[0];
+ if (testMode) {
+ while (*argv)
+ printf("%s\n", *argv++);
+ return 0;
+ }
+
+ execv(argv[0], argv);
+ fprintf(stderr, "%s: could not exec '%s': %s\n",
+ argv0, argv[0], strerror(errno));
+ return 1;
+}
+
+static vector<string> stringSplit(const char *source)
+{
+#if defined(_WIN32) || defined(__WIN32__)
+ char listSeparator = ';';
+#else
+ char listSeparator = ':';
+#endif
+
+ vector<string> result;
+ if (!*source)
+ return result;
+
+ while (true) {
+ const char *p = strchr(source, listSeparator);
+ if (!p) {
+ result.push_back(source);
+ return result;
+ }
+
+ result.push_back(string(source, p - source));
+ source = p + 1;
+ }
+ return result;
+}
+
+vector<string> ToolWrapper::searchPaths() const
+{
+ vector<string> paths;
+ if (testMode) {
+ return stringSplit(getenv("QTCHOOSER_PATHS"));
+ } else {
+ // search the XDG config location directories
+ const char *globalDirs = getenv("XDG_CONFIG_DIRS");
+ paths = stringSplit(!globalDirs || !*globalDirs ? "/etc/xdg" : globalDirs);
+
+ string localDir;
+ const char *localDirEnv = getenv("XDG_CONFIG_HOME");
+ if (localDirEnv && *localDirEnv) {
+ localDir = localDirEnv;
+ } else {
+ localDir = getenv("HOME"); // accept empty $HOME too
+ localDir += "/.config";
+ }
+ paths.push_back(localDir);
+
+ for (vector<string>::iterator it = paths.begin(); it != paths.end(); ++it)
+ *it += "/qtchooser/";
+ }
+ return paths;
+}
+
+Sdk ToolWrapper::iterateSdks(const string &targetSdk, VisitFunction visit)
+{
+ vector<string> paths = searchPaths();
+ set<string> seenNames;
+ Sdk sdk;
+ for (vector<string>::iterator it = paths.begin(); it != paths.end(); ++it) {
+ const string &path = *it;
+
+ // no ISO C++ or ISO C API for listing directories, so use POSIX
+ DIR *dir = opendir(path.c_str());
+ if (!dir)
+ continue; // no such dir or not a dir, doesn't matter
+
+ while (struct dirent *d = readdir(dir)) {
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (d->d_type == DT_DIR)
+ continue;
+#endif
+
+ static const char wantedSuffix[] = ".conf";
+ size_t fnamelen = strlen(d->d_name);
+ if (fnamelen < sizeof(wantedSuffix))
+ continue;
+ if (memcmp(d->d_name + fnamelen + 1 - sizeof(wantedSuffix), wantedSuffix, sizeof wantedSuffix - 1) != 0)
+ continue;
+
+ if (seenNames.find(d->d_name) != seenNames.end())
+ continue;
+
+ seenNames.insert(d->d_name);
+ sdk.name = d->d_name;
+ sdk.name.resize(fnamelen + 1 - sizeof wantedSuffix);
+ sdk.configFile = path + PATH_SEP + d->d_name;
+ if ((this->*visit)(targetSdk, sdk))
+ return sdk;
+ }
+
+ closedir(dir);
+ }
+ return Sdk();
+}
+
+Sdk ToolWrapper::selectSdk(const string &targetSdk)
+{
+ Sdk matchedSdk = iterateSdks(targetSdk, &ToolWrapper::matchSdk);
+ if (!matchedSdk.isValid()) {
+ fprintf(stderr, "%s: could not find a Qt installation of '%s'\n", argv0, targetSdk.c_str());
+ }
+ return matchedSdk;
+}
+
+bool ToolWrapper::printSdk(const string &, Sdk &sdk)
+{
+ printf("%s\n", sdk.name.c_str());
+ return false; // continue
+}
+
+bool ToolWrapper::matchSdk(const string &targetSdk, Sdk &sdk)
+{
+ if (targetSdk == sdk.name || (targetSdk.empty() && sdk.name == "default")) {
+ FILE *f = fopen(sdk.configFile.c_str(), "r");
+ if (!f) {
+ fprintf(stderr, "%s: could not open config file '%s': %s\n",
+ argv0, sdk.configFile.c_str(), strerror(errno));
+ exit(1);
+ }
+
+ // read the first two lines.
+ // 1) the first line contains the path to the Qt tools like qmake
+ // 2) the second line contains the path to the Qt libraries
+ // further lines are reserved for future enhancement
+ char buf[PATH_MAX];
+ if (!fgets(buf, PATH_MAX - 1, f)) {
+ fclose(f);
+ return false;
+ }
+ sdk.toolsPath = buf;
+ sdk.toolsPath.erase(sdk.toolsPath.size() - 1); // drop newline
+
+ if (!fgets(buf, PATH_MAX - 1, f)) {
+ fclose(f);
+ return false;
+ }
+ sdk.librariesPath = buf;
+ sdk.librariesPath.erase(sdk.librariesPath.size() - 1); // drop newline
+
+ fclose(f);
+ return true;
+ }
+
+ return false;
+}
+
+static inline bool beginsWith(const char *haystack, const char *needle)
+{
+ return strncmp(haystack, needle, strlen(needle)) == 0;
+}
+
+int main(int argc, char **argv)
+{
+ // search the environment for defaults
+ Mode operatingMode = RunTool;
+ argv0 = basename(argv[0]);
+ const char *targetSdk = getenv("QT_SELECT");
+ const char *targetTool = getenv("QTCHOOSER_RUNTOOL");
+
+#ifdef QTCHOOSER_TEST_MODE
+ testMode = atoi(getenv("QTCHOOSER_TESTMODE")) > 0;
+#endif
+
+ // if the target tool wasn't set in the environment, use argv[0]
+ if (!targetTool || !*targetTool)
+ targetTool = argv0;
+
+ // check the arguments to see if there's an override
+ int optind = 1;
+ for ( ; optind < argc; ++optind) {
+ char *arg = argv[optind];
+ if (*arg == '-') {
+ ++arg;
+ if (*arg == '-')
+ ++arg;
+ if (!*arg) {
+ // -- argument, ends our processing
+ // but skip this dash-dash argument
+ ++optind;
+ break;
+ } else if (beginsWith(arg, "qt")) {
+ // -qtX or -qt=X argument
+ arg += 2;
+ targetSdk = *arg == '=' ? arg + 1 : arg;
+ } else if (beginsWith(arg, "run-tool=")) {
+ // -run-tool= argument
+ targetTool = arg + strlen("run-tool=");
+ operatingMode = RunTool;
+ } else if (strcmp(arg, "list-versions") == 0) {
+ operatingMode = ListVersions;
+ } else if (beginsWith(arg, "print-env")) {
+ operatingMode = PrintEnvironment;
+ } else {
+ // not one of our arguments, must be for the target tool
+ break;
+ }
+ } else {
+ // not one of our arguments, must be for the target tool
+ break;
+ }
+ }
+
+ if (!targetSdk)
+ targetSdk = "";
+
+ ToolWrapper wrapper;
+
+ // dispatch
+ switch (operatingMode) {
+ case RunTool:
+ return wrapper.runTool(targetSdk,
+ targetTool,
+ argv + optind - 1);
+
+ case PrintEnvironment:
+ return wrapper.printEnvironment(targetSdk);
+
+ case ListVersions:
+ return wrapper.listVersions();
+ }
+}