From 47e37635f50c09b4f9a9ee7699e3120bab3e4088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 10 Apr 2016 04:29:29 +0200 Subject: NOISSUE split GUI stuff from logic library --- libraries/logic/java/JavaChecker.cpp | 159 ++++++++++++++++++++++ libraries/logic/java/JavaChecker.h | 54 ++++++++ libraries/logic/java/JavaCheckerJob.cpp | 45 +++++++ libraries/logic/java/JavaCheckerJob.h | 84 ++++++++++++ libraries/logic/java/JavaInstall.cpp | 28 ++++ libraries/logic/java/JavaInstall.h | 38 ++++++ libraries/logic/java/JavaInstallList.cpp | 186 ++++++++++++++++++++++++++ libraries/logic/java/JavaInstallList.h | 71 ++++++++++ libraries/logic/java/JavaUtils.cpp | 219 +++++++++++++++++++++++++++++++ libraries/logic/java/JavaUtils.h | 43 ++++++ libraries/logic/java/JavaVersion.cpp | 112 ++++++++++++++++ libraries/logic/java/JavaVersion.h | 30 +++++ 12 files changed, 1069 insertions(+) create mode 100644 libraries/logic/java/JavaChecker.cpp create mode 100644 libraries/logic/java/JavaChecker.h create mode 100644 libraries/logic/java/JavaCheckerJob.cpp create mode 100644 libraries/logic/java/JavaCheckerJob.h create mode 100644 libraries/logic/java/JavaInstall.cpp create mode 100644 libraries/logic/java/JavaInstall.h create mode 100644 libraries/logic/java/JavaInstallList.cpp create mode 100644 libraries/logic/java/JavaInstallList.h create mode 100644 libraries/logic/java/JavaUtils.cpp create mode 100644 libraries/logic/java/JavaUtils.h create mode 100644 libraries/logic/java/JavaVersion.cpp create mode 100644 libraries/logic/java/JavaVersion.h (limited to 'libraries/logic/java') diff --git a/libraries/logic/java/JavaChecker.cpp b/libraries/logic/java/JavaChecker.cpp new file mode 100644 index 00000000..54d552a9 --- /dev/null +++ b/libraries/logic/java/JavaChecker.cpp @@ -0,0 +1,159 @@ +#include "JavaChecker.h" +#include +#include +#include +#include +#include +#include +#include + +JavaChecker::JavaChecker(QObject *parent) : QObject(parent) +{ +} + +void JavaChecker::performCheck() +{ + QString checkerJar = FS::PathCombine(QCoreApplication::applicationDirPath(), "jars", "JavaCheck.jar"); + + QStringList args; + + process.reset(new QProcess()); + if(m_args.size()) + { + auto extraArgs = Commandline::splitArgs(m_args); + args.append(extraArgs); + } + if(m_minMem != 0) + { + args << QString("-Xms%1m").arg(m_minMem); + } + if(m_maxMem != 0) + { + args << QString("-Xmx%1m").arg(m_maxMem); + } + if(m_permGen != 64) + { + args << QString("-XX:PermSize=%1m").arg(m_permGen); + } + + args.append({"-jar", checkerJar}); + process->setArguments(args); + process->setProgram(m_path); + process->setProcessChannelMode(QProcess::SeparateChannels); + qDebug() << "Running java checker: " + m_path + args.join(" ");; + + connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus))); + connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError))); + connect(process.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(stdoutReady())); + connect(process.get(), SIGNAL(readyReadStandardError()), this, SLOT(stderrReady())); + connect(&killTimer, SIGNAL(timeout()), SLOT(timeout())); + killTimer.setSingleShot(true); + killTimer.start(15000); + process->start(); +} + +void JavaChecker::stdoutReady() +{ + QByteArray data = process->readAllStandardOutput(); + QString added = QString::fromLocal8Bit(data); + added.remove('\r'); + m_stdout += added; +} + +void JavaChecker::stderrReady() +{ + QByteArray data = process->readAllStandardError(); + QString added = QString::fromLocal8Bit(data); + added.remove('\r'); + m_stderr += added; +} + +void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) +{ + killTimer.stop(); + QProcessPtr _process; + _process.swap(process); + + JavaCheckResult result; + { + result.path = m_path; + result.id = m_id; + } + result.errorLog = m_stderr; + qDebug() << "STDOUT" << m_stdout; + qWarning() << "STDERR" << m_stderr; + qDebug() << "Java checker finished with status " << status << " exit code " << exitcode; + + if (status == QProcess::CrashExit || exitcode == 1) + { + qDebug() << "Java checker failed!"; + emit checkFinished(result); + return; + } + + bool success = true; + + QMap results; + QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts); + for(QString line : lines) + { + line = line.trimmed(); + + auto parts = line.split('=', QString::SkipEmptyParts); + if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) + { + success = false; + } + else + { + results.insert(parts[0], parts[1]); + } + } + + if(!results.contains("os.arch") || !results.contains("java.version") || !success) + { + qDebug() << "Java checker failed - couldn't extract required information."; + emit checkFinished(result); + return; + } + + auto os_arch = results["os.arch"]; + auto java_version = results["java.version"]; + bool is_64 = os_arch == "x86_64" || os_arch == "amd64"; + + + result.valid = true; + result.is_64bit = is_64; + result.mojangPlatform = is_64 ? "64" : "32"; + result.realPlatform = os_arch; + result.javaVersion = java_version; + qDebug() << "Java checker succeeded."; + emit checkFinished(result); +} + +void JavaChecker::error(QProcess::ProcessError err) +{ + if(err == QProcess::FailedToStart) + { + killTimer.stop(); + qDebug() << "Java checker has failed to start."; + JavaCheckResult result; + { + result.path = m_path; + result.id = m_id; + } + + emit checkFinished(result); + return; + } +} + +void JavaChecker::timeout() +{ + // NO MERCY. NO ABUSE. + if(process) + { + qDebug() << "Java checker has been killed by timeout."; + process->kill(); + } +} diff --git a/libraries/logic/java/JavaChecker.h b/libraries/logic/java/JavaChecker.h new file mode 100644 index 00000000..650e7ce3 --- /dev/null +++ b/libraries/logic/java/JavaChecker.h @@ -0,0 +1,54 @@ +#pragma once +#include +#include +#include + +#include "multimc_logic_export.h" + +#include "JavaVersion.h" + +class JavaChecker; + +struct MULTIMC_LOGIC_EXPORT JavaCheckResult +{ + QString path; + QString mojangPlatform; + QString realPlatform; + JavaVersion javaVersion; + QString errorLog; + bool valid = false; + bool is_64bit = false; + int id; +}; + +typedef std::shared_ptr QProcessPtr; +typedef std::shared_ptr JavaCheckerPtr; +class MULTIMC_LOGIC_EXPORT JavaChecker : public QObject +{ + Q_OBJECT +public: + explicit JavaChecker(QObject *parent = 0); + void performCheck(); + + QString m_path; + QString m_args; + int m_id = 0; + int m_minMem = 0; + int m_maxMem = 0; + int m_permGen = 64; + +signals: + void checkFinished(JavaCheckResult result); +private: + QProcessPtr process; + QTimer killTimer; + QString m_stdout; + QString m_stderr; +public +slots: + void timeout(); + void finished(int exitcode, QProcess::ExitStatus); + void error(QProcess::ProcessError); + void stdoutReady(); + void stderrReady(); +}; diff --git a/libraries/logic/java/JavaCheckerJob.cpp b/libraries/logic/java/JavaCheckerJob.cpp new file mode 100644 index 00000000..0b040e43 --- /dev/null +++ b/libraries/logic/java/JavaCheckerJob.cpp @@ -0,0 +1,45 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JavaCheckerJob.h" + +#include + +void JavaCheckerJob::partFinished(JavaCheckResult result) +{ + num_finished++; + qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/" + << javacheckers.size(); + emit progress(num_finished, javacheckers.size()); + + javaresults.replace(result.id, result); + + if (num_finished == javacheckers.size()) + { + emit finished(javaresults); + } +} + +void JavaCheckerJob::executeTask() +{ + qDebug() << m_job_name.toLocal8Bit() << " started."; + m_running = true; + for (auto iter : javacheckers) + { + javaresults.append(JavaCheckResult()); + connect(iter.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult))); + iter->performCheck(); + } +} diff --git a/libraries/logic/java/JavaCheckerJob.h b/libraries/logic/java/JavaCheckerJob.h new file mode 100644 index 00000000..aca0d02e --- /dev/null +++ b/libraries/logic/java/JavaCheckerJob.h @@ -0,0 +1,84 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include "JavaChecker.h" +#include "tasks/Task.h" + +class JavaCheckerJob; +typedef std::shared_ptr JavaCheckerJobPtr; + +class JavaCheckerJob : public Task +{ + Q_OBJECT +public: + explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {}; + + bool addJavaCheckerAction(JavaCheckerPtr base) + { + javacheckers.append(base); + total_progress++; + // if this is already running, the action needs to be started right away! + if (isRunning()) + { + setProgress(current_progress, total_progress); + connect(base.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult))); + + base->performCheck(); + } + return true; + } + + JavaCheckerPtr operator[](int index) + { + return javacheckers[index]; + } + ; + JavaCheckerPtr first() + { + if (javacheckers.size()) + return javacheckers[0]; + return JavaCheckerPtr(); + } + int size() const + { + return javacheckers.size(); + } + virtual bool isRunning() const override + { + return m_running; + } + +signals: + void started(); + void finished(QList); + +private slots: + void partFinished(JavaCheckResult result); + +protected: + virtual void executeTask() override; + +private: + QString m_job_name; + QList javacheckers; + QList javaresults; + qint64 current_progress = 0; + qint64 total_progress = 0; + int num_finished = 0; + bool m_running = false; +}; diff --git a/libraries/logic/java/JavaInstall.cpp b/libraries/logic/java/JavaInstall.cpp new file mode 100644 index 00000000..bb262b6e --- /dev/null +++ b/libraries/logic/java/JavaInstall.cpp @@ -0,0 +1,28 @@ +#include "JavaInstall.h" +#include + +bool JavaInstall::operator<(const JavaInstall &rhs) +{ + auto archCompare = Strings::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive); + if(archCompare != 0) + return archCompare < 0; + if(id < rhs.id) + { + return true; + } + if(id > rhs.id) + { + return false; + } + return Strings::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0; +} + +bool JavaInstall::operator==(const JavaInstall &rhs) +{ + return arch == rhs.arch && id == rhs.id && path == rhs.path; +} + +bool JavaInstall::operator>(const JavaInstall &rhs) +{ + return (!operator<(rhs)) && (!operator==(rhs)); +} diff --git a/libraries/logic/java/JavaInstall.h b/libraries/logic/java/JavaInstall.h new file mode 100644 index 00000000..882c7386 --- /dev/null +++ b/libraries/logic/java/JavaInstall.h @@ -0,0 +1,38 @@ +#pragma once + +#include "BaseVersion.h" +#include "JavaVersion.h" + +struct JavaInstall : public BaseVersion +{ + JavaInstall(){} + JavaInstall(QString id, QString arch, QString path) + : id(id), arch(arch), path(path) + { + } + virtual QString descriptor() + { + return id.toString(); + } + + virtual QString name() + { + return id.toString(); + } + + virtual QString typeString() const + { + return arch; + } + + bool operator<(const JavaInstall & rhs); + bool operator==(const JavaInstall & rhs); + bool operator>(const JavaInstall & rhs); + + JavaVersion id; + QString arch; + QString path; + bool recommended = false; +}; + +typedef std::shared_ptr JavaInstallPtr; diff --git a/libraries/logic/java/JavaInstallList.cpp b/libraries/logic/java/JavaInstallList.cpp new file mode 100644 index 00000000..c0729227 --- /dev/null +++ b/libraries/logic/java/JavaInstallList.cpp @@ -0,0 +1,186 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +#include "java/JavaInstallList.h" +#include "java/JavaCheckerJob.h" +#include "java/JavaUtils.h" +#include "MMCStrings.h" +#include "minecraft/VersionFilterData.h" + +JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent) +{ +} + +Task *JavaInstallList::getLoadTask() +{ + return new JavaListLoadTask(this); +} + +const BaseVersionPtr JavaInstallList::at(int i) const +{ + return m_vlist.at(i); +} + +bool JavaInstallList::isLoaded() +{ + return m_loaded; +} + +int JavaInstallList::count() const +{ + return m_vlist.count(); +} + +QVariant JavaInstallList::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() > count()) + return QVariant(); + + auto version = std::dynamic_pointer_cast(m_vlist[index.row()]); + switch (role) + { + case VersionPointerRole: + return qVariantFromValue(m_vlist[index.row()]); + case VersionIdRole: + return version->descriptor(); + case VersionRole: + return version->id.toString(); + case RecommendedRole: + return version->recommended; + case PathRole: + return version->path; + case ArchitectureRole: + return version->arch; + default: + return QVariant(); + } +} + +BaseVersionList::RoleList JavaInstallList::providesRoles() const +{ + return {VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, ArchitectureRole}; +} + + +void JavaInstallList::updateListData(QList versions) +{ + beginResetModel(); + m_vlist = versions; + m_loaded = true; + sortVersions(); + if(m_vlist.size()) + { + auto best = std::dynamic_pointer_cast(m_vlist[0]); + best->recommended = true; + } + endResetModel(); +} + +bool sortJavas(BaseVersionPtr left, BaseVersionPtr right) +{ + auto rleft = std::dynamic_pointer_cast(left); + auto rright = std::dynamic_pointer_cast(right); + return (*rleft) > (*rright); +} + +void JavaInstallList::sortVersions() +{ + beginResetModel(); + std::sort(m_vlist.begin(), m_vlist.end(), sortJavas); + endResetModel(); +} + +JavaListLoadTask::JavaListLoadTask(JavaInstallList *vlist) : Task() +{ + m_list = vlist; + m_currentRecommended = NULL; +} + +JavaListLoadTask::~JavaListLoadTask() +{ +} + +void JavaListLoadTask::executeTask() +{ + setStatus(tr("Detecting Java installations...")); + + JavaUtils ju; + QList candidate_paths = ju.FindJavaPaths(); + + m_job = std::shared_ptr(new JavaCheckerJob("Java detection")); + connect(m_job.get(), SIGNAL(finished(QList)), this, SLOT(javaCheckerFinished(QList))); + connect(m_job.get(), &Task::progress, this, &Task::setProgress); + + qDebug() << "Probing the following Java paths: "; + int id = 0; + for(QString candidate : candidate_paths) + { + qDebug() << " " << candidate; + + auto candidate_checker = new JavaChecker(); + candidate_checker->m_path = candidate; + candidate_checker->m_id = id; + m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker)); + + id++; + } + + m_job->start(); +} + +void JavaListLoadTask::javaCheckerFinished(QList results) +{ + QList candidates; + + qDebug() << "Found the following valid Java installations:"; + for(JavaCheckResult result : results) + { + if(result.valid) + { + JavaInstallPtr javaVersion(new JavaInstall()); + + javaVersion->id = result.javaVersion; + javaVersion->arch = result.mojangPlatform; + javaVersion->path = result.path; + candidates.append(javaVersion); + + qDebug() << " " << javaVersion->id.toString() << javaVersion->arch << javaVersion->path; + } + } + + QList javas_bvp; + for (auto java : candidates) + { + //qDebug() << java->id << java->arch << " at " << java->path; + BaseVersionPtr bp_java = std::dynamic_pointer_cast(java); + + if (bp_java) + { + javas_bvp.append(java); + } + } + + m_list->updateListData(javas_bvp); + emitSucceeded(); +} diff --git a/libraries/logic/java/JavaInstallList.h b/libraries/logic/java/JavaInstallList.h new file mode 100644 index 00000000..cf0e5784 --- /dev/null +++ b/libraries/logic/java/JavaInstallList.h @@ -0,0 +1,71 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "BaseVersionList.h" +#include "tasks/Task.h" + +#include "JavaCheckerJob.h" +#include "JavaInstall.h" + +#include "multimc_logic_export.h" + +class JavaListLoadTask; + +class MULTIMC_LOGIC_EXPORT JavaInstallList : public BaseVersionList +{ + Q_OBJECT +public: + explicit JavaInstallList(QObject *parent = 0); + + virtual Task *getLoadTask() override; + virtual bool isLoaded() override; + virtual const BaseVersionPtr at(int i) const override; + virtual int count() const override; + virtual void sortVersions() override; + + virtual QVariant data(const QModelIndex &index, int role) const override; + virtual RoleList providesRoles() const override; + +public slots: + virtual void updateListData(QList versions) override; + +protected: + QList m_vlist; + + bool m_loaded = false; +}; + +class JavaListLoadTask : public Task +{ + Q_OBJECT + +public: + explicit JavaListLoadTask(JavaInstallList *vlist); + ~JavaListLoadTask(); + + virtual void executeTask(); +public slots: + void javaCheckerFinished(QList results); + +protected: + std::shared_ptr m_job; + JavaInstallList *m_list; + JavaInstall *m_currentRecommended; +}; diff --git a/libraries/logic/java/JavaUtils.cpp b/libraries/logic/java/JavaUtils.cpp new file mode 100644 index 00000000..88996e9f --- /dev/null +++ b/libraries/logic/java/JavaUtils.cpp @@ -0,0 +1,219 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include + +#include +#include "java/JavaUtils.h" +#include "java/JavaCheckerJob.h" +#include "java/JavaInstallList.h" +#include "FileSystem.h" + +JavaUtils::JavaUtils() +{ +} + +JavaInstallPtr JavaUtils::MakeJavaPtr(QString path, QString id, QString arch) +{ + JavaInstallPtr javaVersion(new JavaInstall()); + + javaVersion->id = id; + javaVersion->arch = arch; + javaVersion->path = path; + + return javaVersion; +} + +JavaInstallPtr JavaUtils::GetDefaultJava() +{ + JavaInstallPtr javaVersion(new JavaInstall()); + + javaVersion->id = "java"; + javaVersion->arch = "unknown"; + javaVersion->path = "java"; + + return javaVersion; +} + +#if defined(Q_OS_WIN32) +QList JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName) +{ + QList javas; + + QString archType = "unknown"; + if (keyType == KEY_WOW64_64KEY) + archType = "64"; + else if (keyType == KEY_WOW64_32KEY) + archType = "32"; + + HKEY jreKey; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName.toStdString().c_str(), 0, + KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS) + { + // Read the current type version from the registry. + // This will be used to find any key that contains the JavaHome value. + char *value = new char[0]; + DWORD valueSz = 0; + if (RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz) == + ERROR_MORE_DATA) + { + value = new char[valueSz]; + RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz); + } + + QString recommended = value; + + TCHAR subKeyName[255]; + DWORD subKeyNameSize, numSubKeys, retCode; + + // Get the number of subkeys + RegQueryInfoKey(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, + NULL, NULL); + + // Iterate until RegEnumKeyEx fails + if (numSubKeys > 0) + { + for (int i = 0; i < numSubKeys; i++) + { + subKeyNameSize = 255; + retCode = RegEnumKeyEx(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, + NULL); + if (retCode == ERROR_SUCCESS) + { + // Now open the registry key for the version that we just got. + QString newKeyName = keyName + "\\" + subKeyName; + + HKEY newKey; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, newKeyName.toStdString().c_str(), 0, + KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS) + { + // Read the JavaHome value to find where Java is installed. + value = new char[0]; + valueSz = 0; + if (RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value, + &valueSz) == ERROR_MORE_DATA) + { + value = new char[valueSz]; + RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value, + &valueSz); + + // Now, we construct the version object and add it to the list. + JavaInstallPtr javaVersion(new JavaInstall()); + + javaVersion->id = subKeyName; + javaVersion->arch = archType; + javaVersion->path = + QDir(FS::PathCombine(value, "bin")).absoluteFilePath("javaw.exe"); + javas.append(javaVersion); + } + + RegCloseKey(newKey); + } + } + } + } + + RegCloseKey(jreKey); + } + + return javas; +} + +QList JavaUtils::FindJavaPaths() +{ + QList java_candidates; + + QList JRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); + QList JDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); + QList JRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); + QList JDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); + + java_candidates.append(JRE64s); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe")); + java_candidates.append(JDK64s); + java_candidates.append(JRE32s); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe")); + java_candidates.append(JDK32s); + java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path)); + + QList candidates; + for(JavaInstallPtr java_candidate : java_candidates) + { + if(!candidates.contains(java_candidate->path)) + { + candidates.append(java_candidate->path); + } + } + + return candidates; +} + +#elif defined(Q_OS_MAC) +QList JavaUtils::FindJavaPaths() +{ + QList javas; + javas.append(this->GetDefaultJava()->path); + javas.append("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java"); + javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java"); + javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java"); + QDir libraryJVMDir("/Library/Java/JavaVirtualMachines/"); + QStringList libraryJVMJavas = libraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + foreach (const QString &java, libraryJVMJavas) { + javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); + javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/jre/bin/java"); + } + QDir systemLibraryJVMDir("/System/Library/Java/JavaVirtualMachines/"); + QStringList systemLibraryJVMJavas = systemLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + foreach (const QString &java, systemLibraryJVMJavas) { + javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); + javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); + } + return javas; +} + +#elif defined(Q_OS_LINUX) +QList JavaUtils::FindJavaPaths() +{ + qDebug() << "Linux Java detection incomplete - defaulting to \"java\""; + + QList javas; + javas.append(this->GetDefaultJava()->path); + javas.append("/opt/java/bin/java"); + javas.append("/usr/bin/java"); + + return javas; +} +#else +QList JavaUtils::FindJavaPaths() +{ + qDebug() << "Unknown operating system build - defaulting to \"java\""; + + QList javas; + javas.append(this->GetDefaultJava()->path); + + return javas; +} +#endif diff --git a/libraries/logic/java/JavaUtils.h b/libraries/logic/java/JavaUtils.h new file mode 100644 index 00000000..3fb88341 --- /dev/null +++ b/libraries/logic/java/JavaUtils.h @@ -0,0 +1,43 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "JavaCheckerJob.h" +#include "JavaChecker.h" +#include "JavaInstallList.h" + +#ifdef Q_OS_WIN +#include +#endif + +#include "multimc_logic_export.h" + +class MULTIMC_LOGIC_EXPORT JavaUtils : public QObject +{ + Q_OBJECT +public: + JavaUtils(); + + JavaInstallPtr MakeJavaPtr(QString path, QString id = "unknown", QString arch = "unknown"); + QList FindJavaPaths(); + JavaInstallPtr GetDefaultJava(); + +#ifdef Q_OS_WIN + QList FindJavaFromRegistryKey(DWORD keyType, QString keyName); +#endif +}; diff --git a/libraries/logic/java/JavaVersion.cpp b/libraries/logic/java/JavaVersion.cpp new file mode 100644 index 00000000..84fc48a4 --- /dev/null +++ b/libraries/logic/java/JavaVersion.cpp @@ -0,0 +1,112 @@ +#include "JavaVersion.h" +#include + +#include +#include + +JavaVersion & JavaVersion::operator=(const QString & javaVersionString) +{ + string = javaVersionString; + + auto getCapturedInteger = [](const QRegularExpressionMatch & match, const QString &what) -> int + { + auto str = match.captured(what); + if(str.isEmpty()) + { + return 0; + } + return str.toInt(); + }; + + QRegularExpression pattern; + if(javaVersionString.startsWith("1.")) + { + pattern = QRegularExpression ("1[.](?[0-9]+)([.](?[0-9]+))?(_(?[0-9]+)?)?(-(?[a-zA-Z0-9]+))?"); + } + else + { + pattern = QRegularExpression("(?[0-9]+)([.](?[0-9]+))?([.](?[0-9]+))?(-(?[a-zA-Z0-9]+))?"); + } + + auto match = pattern.match(string); + parseable = match.hasMatch(); + major = getCapturedInteger(match, "major"); + minor = getCapturedInteger(match, "minor"); + security = getCapturedInteger(match, "security"); + prerelease = match.captured("prerelease"); + return *this; +} + +JavaVersion::JavaVersion(const QString &rhs) +{ + operator=(rhs); +} + +QString JavaVersion::toString() +{ + return string; +} + +bool JavaVersion::requiresPermGen() +{ + if(parseable) + { + return major < 8; + } + return true; +} + +bool JavaVersion::operator<(const JavaVersion &rhs) +{ + if(parseable && rhs.parseable) + { + if(major < rhs.major) + return true; + if(major > rhs.major) + return false; + if(minor < rhs.minor) + return true; + if(minor > rhs.minor) + return false; + if(security < rhs.security) + return true; + if(security > rhs.security) + return false; + + // everything else being equal, consider prerelease status + bool thisPre = !prerelease.isEmpty(); + bool rhsPre = !rhs.prerelease.isEmpty(); + if(thisPre && !rhsPre) + { + // this is a prerelease and the other one isn't -> lesser + return true; + } + else if(!thisPre && rhsPre) + { + // this isn't a prerelease and the other one is -> greater + return false; + } + else if(thisPre && rhsPre) + { + // both are prereleases - use natural compare... + return Strings::naturalCompare(prerelease, rhs.prerelease, Qt::CaseSensitive) < 0; + } + // neither is prerelease, so they are the same -> this cannot be less than rhs + return false; + } + else return Strings::naturalCompare(string, rhs.string, Qt::CaseSensitive) < 0; +} + +bool JavaVersion::operator==(const JavaVersion &rhs) +{ + if(parseable && rhs.parseable) + { + return major == rhs.major && minor == rhs.minor && security == rhs.security && prerelease == rhs.prerelease; + } + return string == rhs.string; +} + +bool JavaVersion::operator>(const JavaVersion &rhs) +{ + return (!operator<(rhs)) && (!operator==(rhs)); +} diff --git a/libraries/logic/java/JavaVersion.h b/libraries/logic/java/JavaVersion.h new file mode 100644 index 00000000..f9a733d3 --- /dev/null +++ b/libraries/logic/java/JavaVersion.h @@ -0,0 +1,30 @@ +#pragma once + +#include "multimc_logic_export.h" +#include + +class MULTIMC_LOGIC_EXPORT JavaVersion +{ + friend class JavaVersionTest; +public: + JavaVersion() {}; + JavaVersion(const QString & rhs); + + JavaVersion & operator=(const QString & rhs); + + bool operator<(const JavaVersion & rhs); + bool operator==(const JavaVersion & rhs); + bool operator>(const JavaVersion & rhs); + + bool requiresPermGen(); + + QString toString(); + +private: + QString string; + int major = 0; + int minor = 0; + int security = 0; + bool parseable = false; + QString prerelease; +}; -- cgit v1.2.3