Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSIX #5330

Merged
merged 3 commits into from
Nov 28, 2023
Merged

MSIX #5330

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ set(CMAKE_AUTOUIC ON)
# Later, I would try tor remove this, for now, it's necessary because Conan
# tends to mess up the directory structure
if(WIN32)
include(InstallRequiredSystemLibraries)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO
Expand Down
115 changes: 77 additions & 38 deletions Desktop/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ const std::string jaspExtension = ".jasp",
removeJunctionsArg = "--removeJunctions";

#ifdef _WIN32

#include "utilities/dynamicruntimeinfo.h"
#include "utilities/appdirs.h"
#include "utilities/processhelper.h"
// This function simply sets the proper environment of jaspengine, and starts it in junction-fixing mode or remove-junction mode.
// The junction-fixining mode is called after the installer runs to fix the junctions in Modules that actually point to renv-cache instead of nowhere
Expand All @@ -53,29 +54,57 @@ bool runJaspEngineJunctionFixer(int argc, char *argv[], bool removeJunctions = f
QApplication * app = exitAfterwards ? new QApplication(argc, argv) : nullptr;
QProcessEnvironment env = ProcessHelper::getProcessEnvironmentForJaspEngine();
QString workDir = QFileInfo( QCoreApplication::applicationFilePath() ).absoluteDir().absolutePath();

QProcess engine;

engine.setProcessChannelMode(QProcess::ForwardedChannels);
engine.setProcessEnvironment(env);
engine.setWorkingDirectory(workDir);
engine.setProgram("JASPEngine.exe");
if (removeJunctions) engine.setArguments({"--removeJunctions", workDir});
else engine.setArguments({"--recreateJunctions", workDir});

QDir modulesDir(AppDirs::bundledModulesDir());
if(modulesDir.exists() && AppDirs::bundledModulesDir().contains("Modules", Qt::CaseInsensitive))
{
std::function<void(QDir)> removeDir = [&](QDir x) -> void {
for(const auto& entry : x.entryInfoList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files))
{
if(entry.isFile() || entry.isSymLink()) entry.absoluteDir().rmdir(entry.fileName());
else if(entry.isJunction()) entry.absoluteDir().rmdir(entry.fileName());
else if(entry.isDir()) removeDir(QDir(entry.absoluteFilePath()));
}
x.rmdir(".");
};
removeDir(modulesDir);
}

if (removeJunctions)
{
std::cout << "Junctions removal " << (!modulesDir.exists() ? "succeeded" : "failed") << std::endl;
return !modulesDir.exists();
}
else
{
bool created = QDir().mkpath(modulesDir.absolutePath());
if(created)
std::cout << "Junction folder created" << std::endl;
engine.setArguments({"--recreateJunctions", workDir + "/Modules/", modulesDir.absolutePath(), workDir + "/junctions.rds"});
}
engine.start();

if(!engine.waitForStarted()) { std::cerr << "JASPEngine failed to start for junctions!" << std::endl; exit(2); }
//Something like 10 minutes tops should be more than enough for the junctions to be replaced and otherwise it probably crashed?
if(!engine.waitForFinished(600000)) { std::cerr << "JASPEngine started but timed out before finishing junctions!" << std::endl; exit(3); }

bool worked = engine.exitCode() == 0;

std::cout << (removeJunctions ? "Removing" : "Replacing") << " junctions with JASPEngine seems to have " << (worked ? "worked." : "failed.") << std::endl;
//log our success so we dont do it a second time
if(worked)
worked = worked && DynamicRuntimeInfo::getInstance()->writeDynamicRuntimeInfoFile();

std::cout << "Replacing junctions with JASPEngine seems to have " << (worked ? "worked." : "failed.") << std::endl;


if(exitAfterwards)
exit(engine.exitCode());

return worked;
}

Expand Down Expand Up @@ -184,7 +213,7 @@ void parseArguments(int argc, char *argv[], std::string & filePath, bool & unitT
{
std::cerr << "Directory to write reports to " << testMe.absolutePath().toStdString() << " does not exist and cannot be created!" << std::endl;
letsExplainSomeThings = true;
}
}
else
reportingDir = testMe.absolutePath();
}
Expand Down Expand Up @@ -252,9 +281,9 @@ void parseArguments(int argc, char *argv[], std::string & filePath, bool & unitT
else
{
//Check whether it can be parsed as a json and if so assume it is a database connection json as returned by DatabaseConnectionInfo

Json::Reader jsonReader;

if(!jsonReader.parse(args[arg], dbJson))
{
std::cerr << "File to open " << args[arg] << " does not exist (and also is not a (database) json)!" << std::endl;
Expand All @@ -265,7 +294,7 @@ void parseArguments(int argc, char *argv[], std::string & filePath, bool & unitT
}
}
}

if(filePath == "" && reportingDir != "")
{
std::cerr << "If you want JASP to run in reportingmode you should also give it a jaspfile to run off." << std::endl;
Expand Down Expand Up @@ -383,21 +412,21 @@ int main(int argc, char *argv[])
QCoreApplication::setOrganizationName("JASP");
QCoreApplication::setOrganizationDomain("jasp-stats.org");
QCoreApplication::setApplicationName("JASP");

parseArguments(argc, argv, filePath, unitTest, dirTest, timeOut, save, logToFile, hideJASP, safeGraphics, dbJson, reportingDir);

if(safeGraphics) Settings::setValue(Settings::SAFE_GRAPHICS_MODE, true);
else safeGraphics = Settings::value(Settings::SAFE_GRAPHICS_MODE).toBool();

if(reportingDir!="") Settings::setValue(Settings::REPORT_SHOW, true);

QString filePathQ(QSTRING_FILE_ARG(filePath.c_str()));

//Now, to allow us to add some arguments we store the ones we got in a vector
std::vector<std::string> args(argv, argv + argc);

// std::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16<wchar_t>() ) ); This is not needed anymore since we set the locale to UTF8

if(!dirTest)
//try
{
Expand All @@ -409,13 +438,13 @@ int main(int argc, char *argv[])
char dst[] = "LIBGL_ALWAYS_SOFTWARE=1";
putenv(dst);
}

if(hideJASP)
{
args.push_back("-platform");
args.push_back("minimal");
}

if(hideJASP)
{
args.push_back("-platform");
Expand Down Expand Up @@ -461,37 +490,47 @@ int main(int argc, char *argv[])
std::cout << "QtWebEngineQuick initialized" << std::endl;

Application a(argvsize, argvs);

std::cout << "Application initialized" << std::endl;

PlotSchemeHandler plotSchemeHandler; //Makes sure plots can still be loaded in webengine with Qt6
ImgSchemeHandler imgSchemeHandler;

a.init(filePathQ, unitTest, timeOut, save, logToFile, dbJson, reportingDir);

#ifdef _WIN32
// Since we introduced renv to JASP the win installer needs to recreate the junctions from Modules -> renv-cache on install. Because they do not support relative paths
// For this JASP has the --junctions argument, and is run by JASP-*.msi during install to make sure everything is ready.
// However, we also want to support ZIP distributions of jasp, and there is no installer. But the good thing is it also means the user has write access to the main folder. Which means we can fix it now.
QFile junctionsCreationLog("junctions-recreated-successfully.log");
QFile jaspEngine("JASPEngine.exe");
if(jaspEngine.exists() && !junctionsCreationLog.exists())
auto runtimeEnv = DynamicRuntimeInfo::getInstance()->getRuntimeEnvironmentAsString();
Log::log() << "Runtime Environment: " << runtimeEnv << std::endl;

// Since we introduced renv to JASP, we need to recreate the junctions from Modules -> renv-cache on first run. Because windows does not support proper symlinks on user perms
// For this JASP has the --junctions argument, and is run on first execution of a specific jasp version on a system.
Log::log() << "Checking if we need to recreate junctions or not" << std::endl;
if(!DynamicRuntimeInfo::getInstance()->bundledModulesInitialized())
{
QMessageBox::information(nullptr, Application::tr("One time setup for JASP Modules"), Application::tr("JASP has been installed from a zip and it needs to recreate certain paths for your analyses to work.\n\nPlease be patient and wait for the application to show before attempting to start JASP again."));

Log::log() << "We need to recreate junctions!" << std::endl;

QMessageBox *msgBox = new QMessageBox(nullptr);
msgBox->setIcon( QMessageBox::Information );
msgBox->setText("JASP is setting a few things up. Just a moment please");
QPushButton *btn = msgBox->addButton( "Ok", QMessageBox::AcceptRole );
msgBox->setAttribute(Qt::WA_DeleteOnClose); // delete pointer after close
msgBox->setModal(false);
msgBox->show();

if(!runJaspEngineJunctionFixer(argc, argv, false, false))
{
std::cerr << "Modules folder missing and couldn't be created!\nContact the JASP team for support, or try the MSI." << std::endl;
{
std::cerr << "Modules folder missing and couldn't be created!\nContact the JASP team for support." << std::endl;
exit(254);
}
msgBox->hide();
}
#endif

a.init(filePathQ, unitTest, timeOut, save, logToFile, dbJson, reportingDir);

try


try
{
std::cout << "Entering eventloop" << std::endl;

int exitCode = a.exec();
JASPTIMER_STOP("JASP");
JASPTIMER_PRINTALL();
Expand Down
62 changes: 21 additions & 41 deletions Engine/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,27 @@ void openConsoleOutput(unsigned long slaveNo, unsigned parentPID)
#ifdef _WIN32
int wmain( int argc, wchar_t *argv[ ], wchar_t *envp[ ] )
{
if(argc == 3)
{
std::string arg1(Utils::wstringToString(argv[1])), arg2(Utils::wstringToString(argv[2]));
if(arg1 == "--collectJunctions")
{
Log::log() << "Engine started for junctions, got folder '" << arg2 << "'!" << std::endl;
rbridge_junctionHelper(true, arg2, "", "");
exit(0);
}
}
else if(argc == 5)
{
std::string arg1(Utils::wstringToString(argv[1])), arg2(Utils::wstringToString(argv[2])), arg3(Utils::wstringToString(argv[3])), arg4(Utils::wstringToString(argv[4]));
if(arg1 == "--recreateJunctions")
{
Log::log() << "Engine started for junctions, got folder '" << arg2 << "'!" << std::endl;
rbridge_junctionHelper(false, arg2, arg3, arg4);
exit(0);
}
}

if(argc > 4)
{
unsigned long slaveNo = wcstoul(argv[1], NULL, 10),
Expand Down Expand Up @@ -127,47 +148,6 @@ int main(int argc, char *argv[])

exit(0);
}
#ifdef _WIN32
else if(argc == 3)
{
std::string arg1(Utils::wstringToString(argv[1])), arg2(Utils::wstringToString(argv[2]));
const std::string junctionCollectArg("--collectJunctions"), junctionRecreateArg("--recreateJunctions"), junctionRemoveArg("--removeJunctions");

if(arg1 == junctionRemoveArg || arg1 == junctionRecreateArg) //Also remove the old modules if it already exists and we are asked to recreate them, because it might be the old ones (previous install)
{
std::string junctionsCreationLog("junctions-recreated-successfully.log");
std::filesystem::path junctionsCreationLogPath = Utils::osPath(junctionsCreationLog);

Log::log() << "Before removing junctions" << std::endl;

if(exists(junctionsCreationLogPath))
remove(junctionsCreationLogPath);

std::string modulesFolder("Modules");
Log::log() << "Engine started to remove the Modules folder" << std::endl;
std::filesystem::path modulesPath = Utils::osPath(modulesFolder);

if(exists(modulesPath)) {
Log::log() << "Module Path exists" << std::endl;
for(const std::filesystem::directory_entry & entry : std::filesystem::directory_iterator(modulesPath))
if(entry.path().string().find("\\jasp") != std::string::npos)
remove_all(entry);
Log::log() << "Modules folder should be clean" << std::endl;
}
else if(arg1 == junctionRemoveArg)
Log::log() << "Error: Could not find the Modules folder" << std::endl;

}

if(arg1 == junctionCollectArg || arg1 == junctionRecreateArg)
{
Log::log() << "Engine started for junctions, got folder '" << arg2 << "'!" << std::endl;
rbridge_junctionHelper(arg1 == junctionCollectArg, arg2);
}

exit(0);
}
#endif

Log::log() << "Engine started in testing mode because it didn't receive any count of arguments otherwise, (1, 3 or 4), it got " << argc << " instead." << std::endl;

Expand Down
4 changes: 2 additions & 2 deletions Engine/otoolstuff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ void _moduleLibraryFixer(const std::string & moduleLibraryPath, bool engineCall,
{"libtbbmalloc_proxy.dylib", "@executable_path/../Modules/" + jaspModuleName + "/RcppParallel/lib/libtbbmalloc_proxy.dylib"},
{"libtbb.dylib", "@executable_path/../Modules/" + jaspModuleName + "/RcppParallel/lib/libtbb.dylib"}
#ifndef __aarch64__
,{"libgfortran.dylib", framework_resources + "opt/local/gfortran/lib/libgfortran.dylib"}
,{"libquadmath.dylib", framework_resources + "opt/local/gfortran/lib/libquadmath.dylib"}
,{"libgfortran.dylib", framework_resources + "lib/libgfortran.5.dylib"}
,{"libquadmath.dylib", framework_resources + "lib/libquadmath.0.dylib"}
#endif
};

Expand Down
4 changes: 2 additions & 2 deletions Engine/rbridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ void rbridge_init(sendFuncDef sendToDesktopFunction, pollMessagesFuncDef pollMes

}

void rbridge_junctionHelper(bool collectNotRestore, const std::string & folder)
void rbridge_junctionHelper(bool collectNotRestore, const std::string & modulesFolder, const std::string& linkFolder, const std::string& junctionFilePath)
{
jaspRCPP_junctionHelper(collectNotRestore, folder.c_str());
jaspRCPP_junctionHelper(collectNotRestore, modulesFolder.c_str(), linkFolder.c_str(), junctionFilePath.c_str());
}

void rbridge_setDataSetSource( std::function<DataSet* ()> source) { rbridge_dataSetSource = source; }
Expand Down
2 changes: 1 addition & 1 deletion Engine/rbridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ extern "C" {
typedef std::function<std::string (const std::string &, int progress)> RCallback;

void rbridge_init(sendFuncDef sendToDesktopFunction, pollMessagesFuncDef pollMessagesFunction, ColumnEncoder * encoder, const char * resultFont);
void rbridge_junctionHelper(bool collectNotRestore, const std::string & folder);
void rbridge_junctionHelper(bool collectNotRestore, const std::string & modulesFolder, const std::string& linkFolder, const std::string& junctionFilePath);

void rbridge_setFileNameSource( std::function<void(const std::string &, std::string &, std::string &)> source);
void rbridge_setSpecificFileNameSource( std::function<void(const std::string &, std::string &, std::string &)> source);
Expand Down
19 changes: 12 additions & 7 deletions Modules/install-jaspBase.R.in
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,20 @@ if (md5SumsChanged(modulePkg, moduleLibrary)) {

}

#This is necessary because of conflicts with Matrix dep of base R lib.
#Can be removed when a new version of R updates its Matrix
if (!@IS_FLATPAK_USED@) {

if (Sys.info()["sysname"] == "Darwin") {
options(pkgType = "source")
}

renv::install("Matrix", library = moduleLibrary, prompt = prompt)
}

# Converting the absolute symlinks to relative symlinks on macOS
# Todo, I can do this using CMake like I do on Windows
if (Sys.info()["sysname"] == "Darwin") {
source('@MODULES_BINARY_PATH@/symlinkTools.R')
convertAbsoluteSymlinksToRelative('@R_LIBRARY_PATH@', '@MODULES_RENV_CACHE_PATH@')
}

#This is necessary because of conflicts with Matrix dep of base R lib.
#Can be removed when a new version of R updates its Matrix
if (@IS_LINUX_LOCAL_BUILD@) {
renv::install("Matrix", type="source")
}
}
Loading
Loading