Better (but unsorted) Java detection

This commit is contained in:
Sky 2013-12-11 03:54:39 +00:00
parent ab69c1b9e6
commit afa5e14e20
13 changed files with 338 additions and 87 deletions

View File

@ -388,6 +388,8 @@ logic/NagUtils.h
logic/NagUtils.cpp logic/NagUtils.cpp
logic/SkinUtils.h logic/SkinUtils.h
logic/SkinUtils.cpp logic/SkinUtils.cpp
logic/JavaCheckerJob.h
logic/JavaCheckerJob.cpp
# Assets # Assets
logic/assets/AssetsUtils.h logic/assets/AssetsUtils.h

View File

@ -2,13 +2,23 @@ import java.lang.Integer;
public class JavaCheck public class JavaCheck
{ {
private static final String key = "os.arch"; private static final String[] keys = {"os.arch", "java.version"};
public static void main (String [] args) public static void main (String [] args)
{
int ret = 0;
for(String key : keys)
{ {
String property = System.getProperty(key); String property = System.getProperty(key);
if(property != null)
{
System.out.println(key + "=" + property); System.out.println(key + "=" + property);
if (property != null) }
System.exit(0); else
System.exit(1); {
ret = 1;
}
}
System.exit(ret);
} }
} }

View File

@ -220,7 +220,8 @@ void InstanceSettings::on_javaTestBtn_clicked()
checker.reset(new JavaChecker()); checker.reset(new JavaChecker());
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
SLOT(checkFinished(JavaCheckResult))); SLOT(checkFinished(JavaCheckResult)));
checker->performCheck(ui->javaPathTextBox->text()); checker->path = ui->javaPathTextBox->text();
checker->performCheck();
} }
void InstanceSettings::checkFinished(JavaCheckResult result) void InstanceSettings::checkFinished(JavaCheckResult result)

View File

@ -259,7 +259,8 @@ void SettingsDialog::on_javaTestBtn_clicked()
checker.reset(new JavaChecker()); checker.reset(new JavaChecker());
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
SLOT(checkFinished(JavaCheckResult))); SLOT(checkFinished(JavaCheckResult)));
checker->performCheck(ui->javaPathTextBox->text()); checker->path = ui->javaPathTextBox->text();
checker->performCheck();
} }
void SettingsDialog::checkFinished(JavaCheckResult result) void SettingsDialog::checkFinished(JavaCheckResult result)
@ -271,7 +272,8 @@ void SettingsDialog::checkFinished(JavaCheckResult result)
if (result.is_64bit) if (result.is_64bit)
text += "Using 64bit java.\n"; text += "Using 64bit java.\n";
text += "\n"; text += "\n";
text += "Platform reported: " + result.realPlatform; text += "Platform reported: " + result.realPlatform + "\n";
text += "Java version reported: " + result.javaVersion;
QMessageBox::information(this, tr("Java test success"), text); QMessageBox::information(this, tr("Java test success"), text);
} }
else else

View File

@ -1,6 +1,7 @@
#include "JavaChecker.h" #include "JavaChecker.h"
#include <QFile> #include <QFile>
#include <QProcess> #include <QProcess>
#include <QMap>
#define CHECKER_FILE "JavaChecker.jar" #define CHECKER_FILE "JavaChecker.jar"
@ -8,7 +9,7 @@ JavaChecker::JavaChecker(QObject *parent) : QObject(parent)
{ {
} }
void JavaChecker::performCheck(QString path) void JavaChecker::performCheck()
{ {
if(QFile::exists(CHECKER_FILE)) if(QFile::exists(CHECKER_FILE))
{ {
@ -40,30 +41,53 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
QProcessPtr _process; QProcessPtr _process;
_process.swap(process); _process.swap(process);
if (status == QProcess::CrashExit || exitcode == 1)
{
emit checkFinished({});
return;
}
QString p_stdout = _process->readAllStandardOutput();
auto parts = p_stdout.split('=', QString::SkipEmptyParts);
if (parts.size() != 2 || parts[0] != "os.arch")
{
emit checkFinished({});
return;
}
auto os_arch = parts[1].remove('\n').remove('\r');
bool is_64 = os_arch == "x86_64" || os_arch == "amd64";
JavaCheckResult result; JavaCheckResult result;
{ {
result.path = path;
}
if (status == QProcess::CrashExit || exitcode == 1)
{
emit checkFinished(result);
return;
}
bool success = true;
QString p_stdout = _process->readAllStandardOutput();
QMap<QString, QString> results;
QStringList lines = p_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)
{
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.valid = true;
result.is_64bit = is_64; result.is_64bit = is_64;
result.mojangPlatform = is_64 ? "64" : "32"; result.mojangPlatform = is_64 ? "64" : "32";
result.realPlatform = os_arch; result.realPlatform = os_arch;
} result.javaVersion = java_version;
emit checkFinished(result); emit checkFinished(result);
} }
@ -72,7 +96,13 @@ void JavaChecker::error(QProcess::ProcessError err)
if(err == QProcess::FailedToStart) if(err == QProcess::FailedToStart)
{ {
killTimer.stop(); killTimer.stop();
emit checkFinished({});
JavaCheckResult result;
{
result.path = path;
}
emit checkFinished(result);
return; return;
} }
} }

View File

@ -3,21 +3,28 @@
#include <QTimer> #include <QTimer>
#include <memory> #include <memory>
class JavaChecker;
struct JavaCheckResult struct JavaCheckResult
{ {
QString path;
QString mojangPlatform; QString mojangPlatform;
QString realPlatform; QString realPlatform;
QString javaVersion;
bool valid = false; bool valid = false;
bool is_64bit = false; bool is_64bit = false;
}; };
typedef std::shared_ptr<QProcess> QProcessPtr;
typedef std::shared_ptr<QProcess> QProcessPtr;
typedef std::shared_ptr<JavaChecker> JavaCheckerPtr;
class JavaChecker : public QObject class JavaChecker : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit JavaChecker(QObject *parent = 0); explicit JavaChecker(QObject *parent = 0);
void performCheck(QString path); void performCheck();
QString path;
signals: signals:
void checkFinished(JavaCheckResult result); void checkFinished(JavaCheckResult result);

49
logic/JavaCheckerJob.cpp Normal file
View File

@ -0,0 +1,49 @@
/* Copyright 2013 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 "pathutils.h"
#include "MultiMC.h"
#include "logger/QsLog.h"
void JavaCheckerJob::partFinished(JavaCheckResult result)
{
num_finished++;
QLOG_INFO() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/"
<< javacheckers.size();
emit progress(num_finished, javacheckers.size());
javaresults.append(result);
int result_size = javacheckers.size();
emit progress(num_finished, result_size);
if (num_finished == javacheckers.size())
{
emit finished(javaresults);
}
}
void JavaCheckerJob::start()
{
QLOG_INFO() << m_job_name.toLocal8Bit() << " started.";
m_running = true;
for (auto iter : javacheckers)
{
connect(iter.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult)));
iter->performCheck();
}
}

100
logic/JavaCheckerJob.h Normal file
View File

@ -0,0 +1,100 @@
/* Copyright 2013 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 <QtNetwork>
#include <QLabel>
#include "JavaChecker.h"
#include "logic/tasks/ProgressProvider.h"
class JavaCheckerJob;
typedef std::shared_ptr<JavaCheckerJob> JavaCheckerJobPtr;
class JavaCheckerJob : public ProgressProvider
{
Q_OBJECT
public:
explicit JavaCheckerJob(QString job_name) : ProgressProvider(), 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())
{
emit progress(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 void getProgress(qint64 &current, qint64 &total)
{
current = current_progress;
total = total_progress;
}
;
virtual QString getStatus() const
{
return m_job_name;
}
;
virtual bool isRunning() const
{
return m_running;
}
;
signals:
void started();
void progress(int current, int total);
void finished(QList<JavaCheckResult>);
public
slots:
virtual void start();
// FIXME: implement
virtual void abort() {};
private
slots:
void partFinished(JavaCheckResult result);
private:
QString m_job_name;
QList<JavaCheckerPtr> javacheckers;
QList<JavaCheckResult> javaresults;
qint64 current_progress = 0;
qint64 total_progress = 0;
int num_finished = 0;
bool m_running = false;
};

View File

@ -26,11 +26,24 @@
#include "JavaUtils.h" #include "JavaUtils.h"
#include "logger/QsLog.h" #include "logger/QsLog.h"
#include "gui/dialogs/VersionSelectDialog.h" #include "gui/dialogs/VersionSelectDialog.h"
#include "JavaCheckerJob.h"
#include "lists/JavaVersionList.h"
JavaUtils::JavaUtils() JavaUtils::JavaUtils()
{ {
} }
JavaVersionPtr JavaUtils::MakeJavaPtr(QString path, QString id, QString arch)
{
JavaVersionPtr javaVersion(new JavaVersion());
javaVersion->id = id;
javaVersion->arch = arch;
javaVersion->path = path;
return javaVersion;
}
JavaVersionPtr JavaUtils::GetDefaultJava() JavaVersionPtr JavaUtils::GetDefaultJava()
{ {
JavaVersionPtr javaVersion(new JavaVersion()); JavaVersionPtr javaVersion(new JavaVersion());
@ -38,7 +51,6 @@ JavaVersionPtr JavaUtils::GetDefaultJava()
javaVersion->id = "java"; javaVersion->id = "java";
javaVersion->arch = "unknown"; javaVersion->arch = "unknown";
javaVersion->path = "java"; javaVersion->path = "java";
javaVersion->recommended = false;
return javaVersion; return javaVersion;
} }
@ -112,7 +124,6 @@ QList<JavaVersionPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
javaVersion->arch = archType; javaVersion->arch = archType;
javaVersion->path = javaVersion->path =
QDir(PathCombine(value, "bin")).absoluteFilePath("java.exe"); QDir(PathCombine(value, "bin")).absoluteFilePath("java.exe");
javaVersion->recommended = (recommended == subKeyName);
javas.append(javaVersion); javas.append(javaVersion);
} }
@ -130,7 +141,7 @@ QList<JavaVersionPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
QList<JavaVersionPtr> JavaUtils::FindJavaPaths() QList<JavaVersionPtr> JavaUtils::FindJavaPaths()
{ {
QList<JavaVersionPtr> javas; QList<JavaVersionPtr> candidates;
QList<JavaVersionPtr> JRE64s = this->FindJavaFromRegistryKey( QList<JavaVersionPtr> JRE64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
@ -141,30 +152,21 @@ QList<JavaVersionPtr> JavaUtils::FindJavaPaths()
QList<JavaVersionPtr> JDK32s = this->FindJavaFromRegistryKey( QList<JavaVersionPtr> JDK32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
javas.append(JRE64s); candidates.append(JRE64s);
javas.append(JDK64s); candidates.append(JDK64s);
javas.append(JRE32s); candidates.append(JRE32s);
javas.append(JDK32s); candidates.append(JDK32s);
if (javas.size() <= 0) candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/java.exe"));
{ candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/java.exe"));
QLOG_WARN() << "Failed to find Java in the Windows registry - defaulting to \"java\""; candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/java.exe"));
javas.append(this->GetDefaultJava()); candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/java.exe"));
return javas;
}
QLOG_INFO() << "Found the following Java installations (64 -> 32, JRE -> JDK): "; candidates.append(this->GetDefaultJava());
for (auto &java : javas) return candidates;
{
QString sRec;
if (java->recommended)
sRec = "(Recommended)";
QLOG_INFO() << java->id << java->arch << " at " << java->path << sRec;
}
return javas;
} }
#elif OSX #elif OSX
QList<JavaVersionPtr> JavaUtils::FindJavaPaths() QList<JavaVersionPtr> JavaUtils::FindJavaPaths()
{ {

View File

@ -19,21 +19,23 @@
#include <QWidget> #include <QWidget>
#include <osutils.h> #include <osutils.h>
#include "JavaCheckerJob.h"
#include "logic/lists/JavaVersionList.h" #include "JavaChecker.h"
#include "lists/JavaVersionList.h"
#if WINDOWS #if WINDOWS
#include <windows.h> #include <windows.h>
#endif #endif
class JavaUtils class JavaUtils : public QObject
{ {
Q_OBJECT
public: public:
JavaUtils(); JavaUtils();
JavaVersionPtr MakeJavaPtr(QString path, QString id = "unknown", QString arch = "unknown");
QList<JavaVersionPtr> FindJavaPaths(); QList<JavaVersionPtr> FindJavaPaths();
JavaVersionPtr GetDefaultJava(); JavaVersionPtr GetDefaultJava();
private:
#if WINDOWS #if WINDOWS
QList<JavaVersionPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName); QList<JavaVersionPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName);

View File

@ -64,7 +64,8 @@ void OneSixUpdate::executeTask()
checker.reset(new JavaChecker()); checker.reset(new JavaChecker());
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
SLOT(checkFinishedOffline(JavaCheckResult))); SLOT(checkFinishedOffline(JavaCheckResult)));
checker->performCheck(java_path); checker->path = java_path;
checker->performCheck();
return; return;
} }
@ -95,7 +96,8 @@ void OneSixUpdate::checkJavaOnline()
checker.reset(new JavaChecker()); checker.reset(new JavaChecker());
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
SLOT(checkFinishedOnline(JavaCheckResult))); SLOT(checkFinishedOnline(JavaCheckResult)));
checker->performCheck(java_path); checker->path = java_path;
checker->performCheck();
} }
void OneSixUpdate::checkFinishedOnline(JavaCheckResult result) void OneSixUpdate::checkFinishedOnline(JavaCheckResult result)

View File

@ -21,7 +21,8 @@
#include <QRegExp> #include <QRegExp>
#include "logger/QsLog.h" #include "logger/QsLog.h"
#include <logic/JavaUtils.h> #include "logic/JavaCheckerJob.h"
#include "logic/JavaUtils.h"
JavaVersionList::JavaVersionList(QObject *parent) : BaseVersionList(parent) JavaVersionList::JavaVersionList(QObject *parent) : BaseVersionList(parent)
{ {
@ -49,7 +50,7 @@ int JavaVersionList::count() const
int JavaVersionList::columnCount(const QModelIndex &parent) const int JavaVersionList::columnCount(const QModelIndex &parent) const
{ {
return 4; return 3;
} }
QVariant JavaVersionList::data(const QModelIndex &index, int role) const QVariant JavaVersionList::data(const QModelIndex &index, int role) const
@ -75,9 +76,6 @@ QVariant JavaVersionList::data(const QModelIndex &index, int role) const
case 2: case 2:
return version->path; return version->path;
case 3:
return version->recommended ? tr("Yes") : tr("No");
default: default:
return QVariant(); return QVariant();
} }
@ -109,9 +107,6 @@ QVariant JavaVersionList::headerData(int section, Qt::Orientation orientation, i
case 2: case 2:
return "Path"; return "Path";
case 3:
return "Recommended";
default: default:
return QVariant(); return QVariant();
} }
@ -128,9 +123,6 @@ QVariant JavaVersionList::headerData(int section, Qt::Orientation orientation, i
case 2: case 2:
return "Path to this Java version."; return "Path to this Java version.";
case 3:
return "Whether the version is recommended or not.";
default: default:
return QVariant(); return QVariant();
} }
@ -142,15 +134,15 @@ QVariant JavaVersionList::headerData(int section, Qt::Orientation orientation, i
BaseVersionPtr JavaVersionList::getTopRecommended() const BaseVersionPtr JavaVersionList::getTopRecommended() const
{ {
for (int i = 0; i < m_vlist.length(); i++) auto first = m_vlist.first();
if(first != nullptr)
{ {
auto ver = std::dynamic_pointer_cast<JavaVersion>(m_vlist.at(i)); return first;
if (ver->recommended) }
else
{ {
return m_vlist.at(i);
}
}
return BaseVersionPtr(); return BaseVersionPtr();
}
} }
void JavaVersionList::updateListData(QList<BaseVersionPtr> versions) void JavaVersionList::updateListData(QList<BaseVersionPtr> versions)
@ -182,17 +174,66 @@ void JavaListLoadTask::executeTask()
{ {
setStatus("Detecting Java installations..."); setStatus("Detecting Java installations...");
QSet<QString> candidate_paths;
JavaUtils ju; JavaUtils ju;
QList<JavaVersionPtr> javas = ju.FindJavaPaths();
QList<JavaVersionPtr> candidates = ju.FindJavaPaths();
for(JavaVersionPtr &candidate : candidates)
{
candidate_paths.insert(candidate->path);
}
auto job = new JavaCheckerJob("Java detection");
connect(job, SIGNAL(finished(QList<JavaCheckResult>)), this, SLOT(javaCheckerFinished(QList<JavaCheckResult>)));
connect(job, SIGNAL(progress(int, int)), this, SLOT(checkerProgress(int, int)));
for(const QString candidate : candidate_paths)
{
auto candidate_checker = new JavaChecker();
candidate_checker->path = candidate;
job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker));
}
QLOG_DEBUG() << "Starting java checker job with" << job->size() << "candidates";
job->start();
}
void JavaListLoadTask::checkerProgress(int current, int total)
{
float progress = (current * 100.0) / (current + total);
this->setProgress((int) progress);
}
void JavaListLoadTask::javaCheckerFinished(QList<JavaCheckResult> results)
{
QList<JavaVersionPtr> candidates;
QLOG_DEBUG() << "Got Java checker results:";
for(JavaCheckResult result : results)
{
if(result.valid)
{
JavaVersionPtr javaVersion(new JavaVersion());
javaVersion->id = result.javaVersion;
javaVersion->arch = result.mojangPlatform;
javaVersion->path = result.path;
candidates.append(javaVersion);
QLOG_DEBUG() << javaVersion->id << javaVersion->arch << javaVersion->path;
}
}
QList<BaseVersionPtr> javas_bvp; QList<BaseVersionPtr> javas_bvp;
for (int i = 0; i < javas.length(); i++) for (auto &java : candidates)
{ {
BaseVersionPtr java = std::dynamic_pointer_cast<BaseVersion>(javas.at(i)); //QLOG_INFO() << java->id << java->arch << " at " << java->path;
BaseVersionPtr bp_java = std::dynamic_pointer_cast<BaseVersion>(java);
if (java) if (bp_java)
{ {
javas_bvp.append(java); javas_bvp.append(bp_java);
} }
} }

View File

@ -20,6 +20,7 @@
#include "BaseVersionList.h" #include "BaseVersionList.h"
#include "logic/tasks/Task.h" #include "logic/tasks/Task.h"
#include "logic/JavaCheckerJob.h"
class JavaListLoadTask; class JavaListLoadTask;
@ -43,7 +44,6 @@ struct JavaVersion : public BaseVersion
QString id; QString id;
QString arch; QString arch;
QString path; QString path;
bool recommended;
}; };
typedef std::shared_ptr<JavaVersion> JavaVersionPtr; typedef std::shared_ptr<JavaVersion> JavaVersionPtr;
@ -85,6 +85,9 @@ public:
~JavaListLoadTask(); ~JavaListLoadTask();
virtual void executeTask(); virtual void executeTask();
public slots:
void javaCheckerFinished(QList<JavaCheckResult> results);
void checkerProgress(int current, int total);
protected: protected:
JavaVersionList *m_list; JavaVersionList *m_list;