diff --git a/extras/Projucer/Builds/LinuxMakefile/Makefile b/extras/Projucer/Builds/LinuxMakefile/Makefile
index 25b490460d..4aa68333ba 100644
--- a/extras/Projucer/Builds/LinuxMakefile/Makefile
+++ b/extras/Projucer/Builds/LinuxMakefile/Makefile
@@ -113,6 +113,7 @@ OBJECTS_APP := \
$(JUCE_OBJDIR)/jucer_CodeHelpers_1e797672.o \
$(JUCE_OBJDIR)/jucer_FileHelpers_54f12f83.o \
$(JUCE_OBJDIR)/jucer_MiscUtilities_31fc8dd8.o \
+ $(JUCE_OBJDIR)/jucer_VersionInfo_46f3ed40.o \
$(JUCE_OBJDIR)/jucer_PIPGenerator_fd3402c7.o \
$(JUCE_OBJDIR)/jucer_Icons_d02d18f1.o \
$(JUCE_OBJDIR)/jucer_JucerTreeViewBase_9b9f2ff0.o \
@@ -368,6 +369,11 @@ $(JUCE_OBJDIR)/jucer_MiscUtilities_31fc8dd8.o: ../../Source/Utility/Helpers/juce
@echo "Compiling jucer_MiscUtilities.cpp"
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
+$(JUCE_OBJDIR)/jucer_VersionInfo_46f3ed40.o: ../../Source/Utility/Helpers/jucer_VersionInfo.cpp
+ -$(V_AT)mkdir -p $(JUCE_OBJDIR)
+ @echo "Compiling jucer_VersionInfo.cpp"
+ $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
+
$(JUCE_OBJDIR)/jucer_PIPGenerator_fd3402c7.o: ../../Source/Utility/PIPs/jucer_PIPGenerator.cpp
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
@echo "Compiling jucer_PIPGenerator.cpp"
diff --git a/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj b/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj
index 435da116f6..c744f52bf5 100644
--- a/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj
+++ b/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj
@@ -257,6 +257,10 @@
isa = PBXBuildFile;
fileRef = 486E8D02DAD2A0BF54A901C0;
};
+ 44AD0D81A65C5EAE3BE588FD = {
+ isa = PBXBuildFile;
+ fileRef = FF3A6A384D536E1AEF47CD54;
+ };
638C7247B6DBA67EFE46E124 = {
isa = PBXBuildFile;
fileRef = 191330B20DAC08B890656EA0;
@@ -2092,6 +2096,13 @@
path = "../../Source/Wizards/jucer_TemplateThumbnailsComponent.h";
sourceTree = "SOURCE_ROOT";
};
+ C16F9F479A3A5F6DAD7647A2 = {
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ name = "jucer_VersionInfo.h";
+ path = "../../Source/Utility/Helpers/jucer_VersionInfo.h";
+ sourceTree = "SOURCE_ROOT";
+ };
C187718F7B9EBA88584B43F3 = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.cpp.cpp;
@@ -2582,6 +2593,13 @@
path = "../../Source/Utility/UI/jucer_ProjucerLookAndFeel.h";
sourceTree = "SOURCE_ROOT";
};
+ FF3A6A384D536E1AEF47CD54 = {
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.cpp.cpp;
+ name = "jucer_VersionInfo.cpp";
+ path = "../../Source/Utility/Helpers/jucer_VersionInfo.cpp";
+ sourceTree = "SOURCE_ROOT";
+ };
FF68231DE2B395461009116C = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
@@ -3011,6 +3029,8 @@
A6C4AE13FB409DE414094CFA,
6FD8DBC0FF42C87D8BEE2452,
00515BA4EC5A7D4DC078ED37,
+ FF3A6A384D536E1AEF47CD54,
+ C16F9F479A3A5F6DAD7647A2,
);
name = Helpers;
sourceTree = "";
@@ -3455,6 +3475,7 @@
8BE478303CDF061B72F219E2,
BF913199032B4CE970E82AA3,
25EF9B3FECB4C9F0F522DCAA,
+ 44AD0D81A65C5EAE3BE588FD,
638C7247B6DBA67EFE46E124,
D0E26EB54B0087C8BE3D541E,
468548FB21D264DC12321327,
diff --git a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj
index e768bb253c..d9d33d4e11 100644
--- a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj
+++ b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj
@@ -233,6 +233,7 @@
+
@@ -1624,6 +1625,7 @@
+
diff --git a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters
index b0e8a75e26..4499c317a5 100644
--- a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters
+++ b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters
@@ -496,6 +496,9 @@
Projucer\Utility\Helpers
+
+ Projucer\Utility\Helpers
+
Projucer\Utility\PIPs
@@ -2325,6 +2328,9 @@
Projucer\Utility\Helpers
+
+ Projucer\Utility\Helpers
+
Projucer\Utility\PIPs
diff --git a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj
index acae1ecf21..a05a2df20b 100644
--- a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj
+++ b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj
@@ -233,6 +233,7 @@
+
@@ -1624,6 +1625,7 @@
+
diff --git a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters
index ddb4d98f78..93ec55f171 100644
--- a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters
+++ b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters
@@ -496,6 +496,9 @@
Projucer\Utility\Helpers
+
+ Projucer\Utility\Helpers
+
Projucer\Utility\PIPs
@@ -2325,6 +2328,9 @@
Projucer\Utility\Helpers
+
+ Projucer\Utility\Helpers
+
Projucer\Utility\PIPs
diff --git a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj
index bb2ad0023c..b4f6b4dc0f 100644
--- a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj
+++ b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj
@@ -233,6 +233,7 @@
+
@@ -1624,6 +1625,7 @@
+
diff --git a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters
index cb3b2b843a..5b56f8c3fb 100644
--- a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters
+++ b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters
@@ -496,6 +496,9 @@
Projucer\Utility\Helpers
+
+ Projucer\Utility\Helpers
+
Projucer\Utility\PIPs
@@ -2325,6 +2328,9 @@
Projucer\Utility\Helpers
+
+ Projucer\Utility\Helpers
+
Projucer\Utility\PIPs
diff --git a/extras/Projucer/Projucer.jucer b/extras/Projucer/Projucer.jucer
index 6f4f708036..3766e94f2e 100644
--- a/extras/Projucer/Projucer.jucer
+++ b/extras/Projucer/Projucer.jucer
@@ -636,6 +636,10 @@
file="Source/Utility/Helpers/jucer_TranslationHelpers.h"/>
+
+
> 16;
- int minor = (versionNum & 0x00ff00) >> 8;
- int build = (versionNum & 0x0000ff) >> 0;
-
- return String (major) + '.' + String (minor) + '.' + String (build);
- }
+ if (showAlertWindows)
+ AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
+ "Update Server Communication Error",
+ "Failed to communicate with the JUCE update server.\n"
+ "Please try again in a few minutes.\n\n"
+ "If this problem persists you can download the latest version of JUCE from juce.com");
- String getProductVersionString()
- {
- return formatProductVersion (ProjectInfo::versionNumber);
+ return;
}
- bool isNewVersion (const String& current, const String& other)
+ if (! info->isNewerVersionThanCurrent())
{
- auto currentTokens = StringArray::fromTokens (current, ".", {});
- auto otherTokens = StringArray::fromTokens (other, ".", {});
-
- jassert (currentTokens.size() == 3 && otherTokens.size() == 3);
-
- if (currentTokens[0].getIntValue() == otherTokens[0].getIntValue())
- {
- if (currentTokens[1].getIntValue() == otherTokens[1].getIntValue())
- return currentTokens[2].getIntValue() < otherTokens[2].getIntValue();
-
- return currentTokens[1].getIntValue() < otherTokens[1].getIntValue();
- }
-
- return currentTokens[0].getIntValue() < otherTokens[0].getIntValue();
- }
-}
-
-void LatestVersionCheckerAndUpdater::queryUpdateServer()
-{
- StringPairArray responseHeaders;
-
- URL latestVersionURL ("https://my.roli.com/software_versions/update_to/Projucer/"
- + VersionHelpers::getProductVersionString() + '/' + getOSString()
- + "?language=" + SystemStats::getUserLanguage());
-
- std::unique_ptr inStream (latestVersionURL.createInputStream (false, nullptr, nullptr,
- "X-API-Key: 265441b-343403c-20f6932-76361d\nContent-Type: "
- "application/json\nAccept: application/json; version=1",
- 0, &responseHeaders, &statusCode, 0));
-
- if (threadShouldExit())
+ if (showAlertWindows)
+ AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
+ "No New Version Available",
+ "Your JUCE version is up to date.");
return;
-
- if (inStream.get() != nullptr && (statusCode == 303 || statusCode == 400))
- {
- if (statusCode == 303)
- relativeDownloadPath = responseHeaders["Location"];
-
- jassert (relativeDownloadPath.isNotEmpty());
-
- jsonReply = JSON::parse (inStream->readEntireStreamAsString());
}
- else if (showAlertWindows)
+
+ auto osString = []
{
- if (statusCode == 204)
- AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, "No New Version Available", "Your JUCE version is up to date.");
- else
- AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Network Error", "Could not connect to the web server.\n"
- "Please check your internet connection and try again.");
- }
-}
+ #if JUCE_MAC
+ return "osx";
+ #elif JUCE_WINDOWS
+ return "windows";
+ #elif JUCE_LINUX
+ return "linux";
+ #else
+ jassertfalse;
+ return "Unknown";
+ #endif
+ }();
-void LatestVersionCheckerAndUpdater::processResult()
-{
- if (! jsonReply.isObject())
- return;
+ String requiredFilename ("juce-" + info->versionString + "-" + osString + ".zip");
- if (statusCode == 400)
+ for (auto& asset : info->assets)
{
- auto errorObject = jsonReply.getDynamicObject()->getProperty ("error");
-
- if (errorObject.isObject())
+ if (asset.name == requiredFilename)
{
- auto message = errorObject.getProperty ("message", {}).toString();
+ auto versionString = info->versionString;
+ auto releaseNotes = info->releaseNotes;
+
+ MessageManager::callAsync ([this, versionString, releaseNotes, asset]
+ {
+ askUserAboutNewVersion (versionString, releaseNotes, asset);
+ });
- if (message.isNotEmpty())
- AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "JUCE Updater", message);
+ return;
}
}
- else if (statusCode == 303)
- {
- askUserAboutNewVersion (jsonReply.getProperty ("version", {}).toString(),
- jsonReply.getProperty ("notes", {}).toString());
- }
+
+ if (showAlertWindows)
+ AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
+ "Failed to find any new downloads",
+ "Please try again in a few minutes.");
}
//==============================================================================
@@ -246,14 +192,15 @@ public:
RectanglePlacement::stretchToFit, 1.0f);
}
- static std::unique_ptr launchDialog (const String& newVersion, const String& releaseNotes)
+ static std::unique_ptr launchDialog (const String& newVersionString,
+ const String& releaseNotes)
{
DialogWindow::LaunchOptions options;
- options.dialogTitle = "Download JUCE version " + newVersion + "?";
+ options.dialogTitle = "Download JUCE version " + newVersionString + "?";
options.resizable = false;
- auto* content = new UpdateDialog (newVersion, releaseNotes);
+ auto* content = new UpdateDialog (newVersionString, releaseNotes);
options.content.set (content, true);
std::unique_ptr dialog (options.create());
@@ -292,66 +239,83 @@ private:
DialogWindow* parentWindow = nullptr;
};
-void LatestVersionCheckerAndUpdater::askUserForLocationToDownload()
+void LatestVersionCheckerAndUpdater::askUserForLocationToDownload (const VersionInfo::Asset& asset)
{
- FileChooser chooser ("Please select the location into which you'd like to install the new version",
+ FileChooser chooser ("Please select the location into which you would like to install the new version",
{ getAppSettings().getStoredPath (Ids::jucePath, TargetOS::getThisOS()).get() });
if (chooser.browseForDirectory())
{
auto targetFolder = chooser.getResult();
- if (isJUCEFolder (targetFolder))
+ // By default we will install into 'targetFolder/JUCE', but we should install into
+ // 'targetFolder' if that is an existing JUCE directory.
+ bool willOverwriteJuceFolder = [&targetFolder]
+ {
+ if (isJUCEFolder (targetFolder))
+ return true;
+
+ targetFolder = targetFolder.getChildFile ("JUCE");
+
+ return isJUCEFolder (targetFolder);
+ }();
+
+ auto targetFolderPath = targetFolder.getFullPathName();
+
+ if (willOverwriteJuceFolder)
{
if (targetFolder.getChildFile (".git").isDirectory())
{
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Downloading New JUCE Version",
- "This folder is a GIT repository!\n\nYou should use a \"git pull\" to update it to the latest version.");
+ targetFolderPath + "\n\nis a GIT repository!\n\nYou should use a \"git pull\" to update it to the latest version.");
return;
}
if (! AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, "Overwrite Existing JUCE Folder?",
- String ("Do you want to overwrite the folder:\n\n" + targetFolder.getFullPathName() + "\n\n..with the latest version from juce.com?\n\n"
- "This will move the existing folder to " + targetFolder.getFullPathName() + "_old.")))
+ "Do you want to replace the folder\n\n" + targetFolderPath + "\n\nwith the latest version from juce.com?\n\n"
+ "This will move the existing folder to " + targetFolderPath + "_old."))
{
return;
}
}
- else
+ else if (targetFolder.exists())
{
- targetFolder = targetFolder.getChildFile ("JUCE").getNonexistentSibling();
+ if (! AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, "Existing File Or Directory",
+ "Do you want to move\n\n" + targetFolderPath + "\n\nto\n\n" + targetFolderPath + "_old?"))
+ {
+ return;
+ }
}
- downloadAndInstall (targetFolder);
+ downloadAndInstall (asset, targetFolder);
}
}
-void LatestVersionCheckerAndUpdater::askUserAboutNewVersion (const String& newVersion, const String& releaseNotes)
+void LatestVersionCheckerAndUpdater::askUserAboutNewVersion (const String& newVersionString,
+ const String& releaseNotes,
+ const VersionInfo::Asset& asset)
{
- if (newVersion.isNotEmpty() && releaseNotes.isNotEmpty()
- && VersionHelpers::isNewVersion (VersionHelpers::getProductVersionString(), newVersion))
- {
- dialogWindow = UpdateDialog::launchDialog (newVersion, releaseNotes);
+ dialogWindow = UpdateDialog::launchDialog (newVersionString, releaseNotes);
- if (auto* mm = ModalComponentManager::getInstance())
- mm->attachCallback (dialogWindow.get(), ModalCallbackFunction::create ([this] (int result)
- {
- if (result == 1)
- askUserForLocationToDownload();
+ if (auto* mm = ModalComponentManager::getInstance())
+ mm->attachCallback (dialogWindow.get(),
+ ModalCallbackFunction::create ([this, asset] (int result)
+ {
+ if (result == 1)
+ askUserForLocationToDownload (asset);
- dialogWindow.reset();
- }));
- }
+ dialogWindow.reset();
+ }));
}
//==============================================================================
class DownloadAndInstallThread : private ThreadWithProgressWindow
{
public:
- DownloadAndInstallThread (const URL& u, const File& t, std::function&& cb)
+ DownloadAndInstallThread (const VersionInfo::Asset& a, const File& t, std::function&& cb)
: ThreadWithProgressWindow ("Downloading New Version", true, true),
- downloadURL (u), targetFolder (t), completionCallback (std::move (cb))
+ asset (a), targetFolder (t), completionCallback (std::move (cb))
{
launchThread (3);
}
@@ -368,7 +332,9 @@ private:
result = install (zipData);
if (result.failed())
- MessageManager::callAsync ([result] () { AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Installation Failed", result.getErrorMessage()); });
+ MessageManager::callAsync ([result] { AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
+ "Installation Failed",
+ result.getErrorMessage()); });
else
MessageManager::callAsync (completionCallback);
}
@@ -378,10 +344,7 @@ private:
setStatusMessage ("Downloading...");
int statusCode = 0;
- StringPairArray responseHeaders;
-
- std::unique_ptr inStream (downloadURL.createInputStream (false, nullptr, nullptr, {}, 0,
- &responseHeaders, &statusCode, 0));
+ auto inStream = VersionInfo::createInputStreamForAsset (asset, statusCode);
if (inStream != nullptr && statusCode == 200)
{
@@ -406,53 +369,53 @@ private:
return Result::ok();
}
- return Result::fail ("Failed to download from: " + downloadURL.toString (false));
+ return Result::fail ("Failed to download from: " + asset.url);
}
- Result install (MemoryBlock& data)
+ Result install (const MemoryBlock& data)
{
setStatusMessage ("Installing...");
- auto result = unzipDownload (data);
-
- if (threadShouldExit())
- result = Result::fail ("Cancelled");
-
- if (result.failed())
- return result;
-
- return Result::ok();
- }
-
- Result unzipDownload (const MemoryBlock& data)
- {
MemoryInputStream input (data, false);
ZipFile zip (input);
if (zip.getNumEntries() == 0)
return Result::fail ("The downloaded file was not a valid JUCE file!");
- auto unzipTarget = File::createTempFile ({});
+ struct ScopedDownloadFolder
+ {
+ ScopedDownloadFolder (const File& installTargetFolder)
+ {
+ folder = installTargetFolder.getSiblingFile (installTargetFolder.getFileNameWithoutExtension() + "_download").getNonexistentSibling();
+ jassert (folder.createDirectory());
+ }
+
+ ~ScopedDownloadFolder() { folder.deleteRecursively(); }
- if (! unzipTarget.createDirectory())
+ File folder;
+ };
+
+ ScopedDownloadFolder unzipTarget (targetFolder);
+
+ if (! unzipTarget.folder.isDirectory())
return Result::fail ("Couldn't create a temporary folder to unzip the new version!");
- auto r = zip.uncompressTo (unzipTarget);
+ auto r = zip.uncompressTo (unzipTarget.folder);
if (r.failed())
- {
- unzipTarget.deleteRecursively();
return r;
- }
+
+ if (threadShouldExit())
+ return Result::fail ("Cancelled");
#if JUCE_LINUX || JUCE_MAC
- r = setFilePermissions (unzipTarget, zip);
+ r = setFilePermissions (unzipTarget.folder, zip);
if (r.failed())
- {
- unzipTarget.deleteRecursively();
return r;
- }
+
+ if (threadShouldExit())
+ return Result::fail ("Cancelled");
#endif
if (targetFolder.exists())
@@ -460,21 +423,15 @@ private:
auto oldFolder = targetFolder.getSiblingFile (targetFolder.getFileNameWithoutExtension() + "_old").getNonexistentSibling();
if (! targetFolder.moveFileTo (oldFolder))
- {
- unzipTarget.deleteRecursively();
return Result::fail ("Could not remove the existing folder!\n\n"
"This may happen if you are trying to download into a directory that requires administrator privileges to modify.\n"
"Please select a folder that is writable by the current user.");
- }
}
- if (! unzipTarget.moveFileTo (targetFolder))
- {
- unzipTarget.deleteRecursively();
+ if (! unzipTarget.folder.getChildFile ("JUCE").moveFileTo (targetFolder))
return Result::fail ("Could not overwrite the existing folder!\n\n"
"This may happen if you are trying to download into a directory that requires administrator privileges to modify.\n"
"Please select a folder that is writable by the current user.");
- }
return Result::ok();
}
@@ -502,7 +459,7 @@ private:
return Result::ok();
}
- URL downloadURL;
+ VersionInfo::Asset asset;
File targetFolder;
std::function completionCallback;
};
@@ -533,9 +490,9 @@ void restartProcess (const File& targetFolder)
}
}
-void LatestVersionCheckerAndUpdater::downloadAndInstall (const File& targetFolder)
+void LatestVersionCheckerAndUpdater::downloadAndInstall (const VersionInfo::Asset& asset, const File& targetFolder)
{
- installer.reset (new DownloadAndInstallThread ({ relativeDownloadPath }, targetFolder,
+ installer.reset (new DownloadAndInstallThread (asset, targetFolder,
[this, targetFolder]
{
installer.reset();
diff --git a/extras/Projucer/Source/Application/jucer_AutoUpdater.h b/extras/Projucer/Source/Application/jucer_AutoUpdater.h
index aa1949376f..2f4b288b58 100644
--- a/extras/Projucer/Source/Application/jucer_AutoUpdater.h
+++ b/extras/Projucer/Source/Application/jucer_AutoUpdater.h
@@ -26,6 +26,8 @@
#pragma once
+#include "../Utility/Helpers/jucer_VersionInfo.h"
+
class DownloadAndInstallThread;
class LatestVersionCheckerAndUpdater : public DeletedAtShutdown,
@@ -43,17 +45,12 @@ public:
private:
//==============================================================================
void run() override;
- void queryUpdateServer();
- void processResult();
- void askUserAboutNewVersion (const String&, const String&);
- void askUserForLocationToDownload();
- void downloadAndInstall (const File&);
+ void askUserAboutNewVersion (const String&, const String&, const VersionInfo::Asset&);
+ void askUserForLocationToDownload (const VersionInfo::Asset&);
+ void downloadAndInstall (const VersionInfo::Asset&, const File&);
//==============================================================================
bool showAlertWindows = false;
- int statusCode = 0;
- String relativeDownloadPath;
- var jsonReply;
std::unique_ptr installer;
std::unique_ptr dialogWindow;
diff --git a/extras/Projucer/Source/LiveBuildEngine/jucer_DownloadCompileEngineThread.cpp b/extras/Projucer/Source/LiveBuildEngine/jucer_DownloadCompileEngineThread.cpp
index b3273dee2f..5b65750066 100644
--- a/extras/Projucer/Source/LiveBuildEngine/jucer_DownloadCompileEngineThread.cpp
+++ b/extras/Projucer/Source/LiveBuildEngine/jucer_DownloadCompileEngineThread.cpp
@@ -27,6 +27,7 @@
#include "../Application/jucer_Headers.h"
#include "jucer_DownloadCompileEngineThread.h"
#include "../LiveBuildEngine/jucer_CompileEngineDLL.h"
+#include "../Utility/Helpers/jucer_VersionInfo.h"
//==============================================================================
bool DownloadCompileEngineThread::downloadAndInstall()
@@ -83,38 +84,60 @@ void DownloadCompileEngineThread::run()
Result DownloadCompileEngineThread::download (MemoryBlock& dest)
{
- int statusCode = 302;
- const int timeoutMs = 10000;
- StringPairArray responseHeaders;
+ auto info = VersionInfo::fetchFromUpdateServer (ProjectInfo::versionString);
- URL url = getDownloadUrl();
- std::unique_ptr in (url.createInputStream (false, nullptr, nullptr,
- String(), timeoutMs, &responseHeaders,
- &statusCode, 0));
+ if (info == nullptr)
+ return Result::fail ("Download error: cannot communicate with server");
- if (in == nullptr || statusCode != 200)
- return Result::fail ("Download error: cannot establish connection");
+ auto requiredAssetName = []
+ {
+ String name ("JUCECompileEngine_");
- MemoryOutputStream mo (dest, true);
+ #if JUCE_MAC
+ name << "osx_";
+ #elif JUCE_WINDOWS
+ name << "windows_";
+ #else
+ jassertfalse;
+ #endif
- int64 size = in->getTotalLength();
- int64 bytesReceived = -1;
- String msg("Downloading... (123)");
+ return name + ProjectInfo::versionString + ".zip";
+ }();
- for (int64 pos = 0; pos < size; pos += bytesReceived)
+ for (auto& asset : info->assets)
{
- setStatusMessage (msg.replace ("123", File::descriptionOfSizeInBytes (pos)));
+ if (asset.name == requiredAssetName)
+ {
+ int statusCode = 0;
+ auto in = VersionInfo::createInputStreamForAsset (asset, statusCode);
+
+ if (in == nullptr || statusCode != 200)
+ return Result::fail ("Download error: cannot establish connection");
+
+ MemoryOutputStream mo (dest, true);
+
+ int64 size = in->getTotalLength();
+ int64 bytesReceived = -1;
+ String msg("Downloading... (123)");
+
+ for (int64 pos = 0; pos < size; pos += bytesReceived)
+ {
+ setStatusMessage (msg.replace ("123", File::descriptionOfSizeInBytes (pos)));
+
+ if (threadShouldExit())
+ return Result::fail ("Download error: operation interrupted");
- if (threadShouldExit())
- return Result::fail ("Download error: operation interrupted");
+ bytesReceived = mo.writeFromInputStream (*in, 8192);
- bytesReceived = mo.writeFromInputStream (*in, 8192);
+ if (bytesReceived == 0)
+ return Result::fail ("Download error: lost connection");
+ }
- if (bytesReceived == 0)
- return Result::fail ("Download error: lost connection");
+ return Result::ok();
+ }
}
- return Result::ok();
+ return Result::fail ("Download error: no downloads available");
}
Result DownloadCompileEngineThread::install (const MemoryBlock& data, File& targetFolder)
@@ -131,21 +154,6 @@ Result DownloadCompileEngineThread::install (const MemoryBlock& data, File& targ
return zip.uncompressTo (targetFolder);
}
-URL DownloadCompileEngineThread::getDownloadUrl()
-{
- String urlStub ("http://assets.roli.com/juce/JUCECompileEngine_");
-
- #if JUCE_MAC
- urlStub << "osx_";
- #elif JUCE_WINDOWS
- urlStub << "windows_";
- #else
- jassertfalse;
- #endif
-
- return urlStub + ProjectInfo::versionString + ".zip";
-}
-
File DownloadCompileEngineThread::getInstallFolder()
{
return CompileEngineDLL::getVersionedUserAppSupportFolder();
diff --git a/extras/Projucer/Source/Utility/Helpers/jucer_VersionInfo.cpp b/extras/Projucer/Source/Utility/Helpers/jucer_VersionInfo.cpp
new file mode 100644
index 0000000000..63cefab1ef
--- /dev/null
+++ b/extras/Projucer/Source/Utility/Helpers/jucer_VersionInfo.cpp
@@ -0,0 +1,115 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2017 - ROLI Ltd.
+
+ JUCE is an open source library subject to commercial or open-source
+ licensing.
+
+ By using JUCE, you agree to the terms of both the JUCE 5 End-User License
+ Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
+ 27th April 2017).
+
+ End User License Agreement: www.juce.com/juce-5-licence
+ Privacy Policy: www.juce.com/juce-5-privacy-policy
+
+ Or: You may also use this code under the terms of the GPL v3 (see
+ www.gnu.org/licenses).
+
+ JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
+ EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
+ DISCLAIMED.
+
+ ==============================================================================
+*/
+
+#include "../../Application/jucer_Headers.h"
+#include "jucer_VersionInfo.h"
+
+std::unique_ptr VersionInfo::fetchFromUpdateServer (const String& versionString)
+{
+ return fetch ("tags/" + versionString);
+}
+
+std::unique_ptr VersionInfo::fetchLatestFromUpdateServer()
+{
+ return fetch ("latest");
+}
+
+std::unique_ptr VersionInfo::createInputStreamForAsset (const Asset& asset, int& statusCode)
+{
+ URL downloadUrl (asset.url);
+ StringPairArray responseHeaders;
+
+ return std::unique_ptr (downloadUrl.createInputStream (false, nullptr, nullptr,
+ "Accept: application/octet-stream",
+ 0, &responseHeaders, &statusCode, 1));
+}
+
+bool VersionInfo::isNewerVersionThanCurrent()
+{
+ jassert (versionString.isNotEmpty());
+
+ auto currentTokens = StringArray::fromTokens (ProjectInfo::versionString, ".", {});
+ auto thisTokens = StringArray::fromTokens (versionString, ".", {});
+
+ jassert (thisTokens.size() == 3 && thisTokens.size() == 3);
+
+ if (currentTokens[0].getIntValue() == thisTokens[0].getIntValue())
+ {
+ if (currentTokens[1].getIntValue() == thisTokens[1].getIntValue())
+ return currentTokens[2].getIntValue() < thisTokens[2].getIntValue();
+
+ return currentTokens[1].getIntValue() < thisTokens[1].getIntValue();
+ }
+
+ return currentTokens[0].getIntValue() < thisTokens[0].getIntValue();
+}
+
+std::unique_ptr VersionInfo::fetch (const String& endpoint)
+{
+ URL latestVersionURL ("https://api.github.com/repos/WeAreROLI/JUCE/releases/" + endpoint);
+ std::unique_ptr inStream (latestVersionURL.createInputStream (false));
+
+ if (inStream == nullptr)
+ return nullptr;
+
+ auto content = inStream->readEntireStreamAsString();
+ auto latestReleaseDetails = JSON::parse (content);
+
+ auto* json = latestReleaseDetails.getDynamicObject();
+
+ if (json == nullptr)
+ return nullptr;
+
+ auto versionString = json->getProperty ("tag_name").toString();
+
+ if (versionString.isEmpty())
+ return nullptr;
+
+ auto* assets = json->getProperty ("assets").getArray();
+
+ if (assets == nullptr)
+ return nullptr;
+
+ auto releaseNotes = json->getProperty ("body").toString();
+ std::vector parsedAssets;
+
+ for (auto& asset : *assets)
+ {
+ if (auto* assetJson = asset.getDynamicObject())
+ {
+ parsedAssets.push_back ({ assetJson->getProperty ("name").toString(),
+ assetJson->getProperty ("url").toString() });
+ jassert (parsedAssets.back().name.isNotEmpty());
+ jassert (parsedAssets.back().url.isNotEmpty());
+ }
+ else
+ {
+ jassertfalse;
+ }
+ }
+
+ return std::unique_ptr (new VersionInfo ({ versionString, releaseNotes, std::move (parsedAssets) }));
+}
diff --git a/extras/Projucer/Source/Utility/Helpers/jucer_VersionInfo.h b/extras/Projucer/Source/Utility/Helpers/jucer_VersionInfo.h
new file mode 100644
index 0000000000..68ec8e710b
--- /dev/null
+++ b/extras/Projucer/Source/Utility/Helpers/jucer_VersionInfo.h
@@ -0,0 +1,54 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2017 - ROLI Ltd.
+
+ JUCE is an open source library subject to commercial or open-source
+ licensing.
+
+ By using JUCE, you agree to the terms of both the JUCE 5 End-User License
+ Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
+ 27th April 2017).
+
+ End User License Agreement: www.juce.com/juce-5-licence
+ Privacy Policy: www.juce.com/juce-5-privacy-policy
+
+ Or: You may also use this code under the terms of the GPL v3 (see
+ www.gnu.org/licenses).
+
+ JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
+ EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
+ DISCLAIMED.
+
+ ==============================================================================
+*/
+
+#pragma once
+
+
+//==============================================================================
+class VersionInfo
+{
+public:
+ struct Asset
+ {
+ const String name;
+ const String url;
+ };
+
+ static std::unique_ptr fetchFromUpdateServer (const String& versionString);
+ static std::unique_ptr fetchLatestFromUpdateServer();
+ static std::unique_ptr createInputStreamForAsset (const Asset& asset, int& statusCode);
+
+ bool isNewerVersionThanCurrent();
+
+ const String versionString;
+ const String releaseNotes;
+ const std::vector assets;
+
+private:
+ VersionInfo() = default;
+
+ static std::unique_ptr fetch (const String&);
+};