Browse Source

Projucer: Updated the autoupdater

tags/2021-05-28
Tom Poole 5 years ago
parent
commit
fadd578b60
14 changed files with 394 additions and 208 deletions
  1. +6
    -0
      extras/Projucer/Builds/LinuxMakefile/Makefile
  2. +21
    -0
      extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj
  3. +2
    -0
      extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj
  4. +6
    -0
      extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters
  5. +2
    -0
      extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj
  6. +6
    -0
      extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters
  7. +2
    -0
      extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj
  8. +6
    -0
      extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters
  9. +4
    -0
      extras/Projucer/Projucer.jucer
  10. +121
    -164
      extras/Projucer/Source/Application/jucer_AutoUpdater.cpp
  11. +5
    -8
      extras/Projucer/Source/Application/jucer_AutoUpdater.h
  12. +44
    -36
      extras/Projucer/Source/LiveBuildEngine/jucer_DownloadCompileEngineThread.cpp
  13. +115
    -0
      extras/Projucer/Source/Utility/Helpers/jucer_VersionInfo.cpp
  14. +54
    -0
      extras/Projucer/Source/Utility/Helpers/jucer_VersionInfo.h

+ 6
- 0
extras/Projucer/Builds/LinuxMakefile/Makefile View File

@@ -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"


+ 21
- 0
extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj View File

@@ -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 = "<group>";
@@ -3455,6 +3475,7 @@
8BE478303CDF061B72F219E2,
BF913199032B4CE970E82AA3,
25EF9B3FECB4C9F0F522DCAA,
44AD0D81A65C5EAE3BE588FD,
638C7247B6DBA67EFE46E124,
D0E26EB54B0087C8BE3D541E,
468548FB21D264DC12321327,


+ 2
- 0
extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj View File

@@ -233,6 +233,7 @@
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_CodeHelpers.cpp"/>
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_FileHelpers.cpp"/>
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_MiscUtilities.cpp"/>
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_VersionInfo.cpp"/>
<ClCompile Include="..\..\Source\Utility\PIPs\jucer_PIPGenerator.cpp"/>
<ClCompile Include="..\..\Source\Utility\UI\jucer_Icons.cpp"/>
<ClCompile Include="..\..\Source\Utility\UI\jucer_JucerTreeViewBase.cpp"/>
@@ -1624,6 +1625,7 @@
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_RelativePath.h"/>
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_TranslationHelpers.h"/>
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_ValueSourceHelpers.h"/>
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_VersionInfo.h"/>
<ClInclude Include="..\..\Source\Utility\PIPs\jucer_PIPGenerator.h"/>
<ClInclude Include="..\..\Source\Utility\UI\PropertyComponents\jucer_ColourPropertyComponent.h"/>
<ClInclude Include="..\..\Source\Utility\UI\PropertyComponents\jucer_FilePathPropertyComponent.h"/>


+ 6
- 0
extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters View File

@@ -496,6 +496,9 @@
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_MiscUtilities.cpp">
<Filter>Projucer\Utility\Helpers</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_VersionInfo.cpp">
<Filter>Projucer\Utility\Helpers</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Utility\PIPs\jucer_PIPGenerator.cpp">
<Filter>Projucer\Utility\PIPs</Filter>
</ClCompile>
@@ -2325,6 +2328,9 @@
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_ValueSourceHelpers.h">
<Filter>Projucer\Utility\Helpers</Filter>
</ClInclude>
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_VersionInfo.h">
<Filter>Projucer\Utility\Helpers</Filter>
</ClInclude>
<ClInclude Include="..\..\Source\Utility\PIPs\jucer_PIPGenerator.h">
<Filter>Projucer\Utility\PIPs</Filter>
</ClInclude>


+ 2
- 0
extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj View File

@@ -233,6 +233,7 @@
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_CodeHelpers.cpp"/>
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_FileHelpers.cpp"/>
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_MiscUtilities.cpp"/>
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_VersionInfo.cpp"/>
<ClCompile Include="..\..\Source\Utility\PIPs\jucer_PIPGenerator.cpp"/>
<ClCompile Include="..\..\Source\Utility\UI\jucer_Icons.cpp"/>
<ClCompile Include="..\..\Source\Utility\UI\jucer_JucerTreeViewBase.cpp"/>
@@ -1624,6 +1625,7 @@
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_RelativePath.h"/>
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_TranslationHelpers.h"/>
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_ValueSourceHelpers.h"/>
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_VersionInfo.h"/>
<ClInclude Include="..\..\Source\Utility\PIPs\jucer_PIPGenerator.h"/>
<ClInclude Include="..\..\Source\Utility\UI\PropertyComponents\jucer_ColourPropertyComponent.h"/>
<ClInclude Include="..\..\Source\Utility\UI\PropertyComponents\jucer_FilePathPropertyComponent.h"/>


+ 6
- 0
extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters View File

@@ -496,6 +496,9 @@
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_MiscUtilities.cpp">
<Filter>Projucer\Utility\Helpers</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_VersionInfo.cpp">
<Filter>Projucer\Utility\Helpers</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Utility\PIPs\jucer_PIPGenerator.cpp">
<Filter>Projucer\Utility\PIPs</Filter>
</ClCompile>
@@ -2325,6 +2328,9 @@
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_ValueSourceHelpers.h">
<Filter>Projucer\Utility\Helpers</Filter>
</ClInclude>
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_VersionInfo.h">
<Filter>Projucer\Utility\Helpers</Filter>
</ClInclude>
<ClInclude Include="..\..\Source\Utility\PIPs\jucer_PIPGenerator.h">
<Filter>Projucer\Utility\PIPs</Filter>
</ClInclude>


+ 2
- 0
extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj View File

@@ -233,6 +233,7 @@
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_CodeHelpers.cpp"/>
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_FileHelpers.cpp"/>
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_MiscUtilities.cpp"/>
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_VersionInfo.cpp"/>
<ClCompile Include="..\..\Source\Utility\PIPs\jucer_PIPGenerator.cpp"/>
<ClCompile Include="..\..\Source\Utility\UI\jucer_Icons.cpp"/>
<ClCompile Include="..\..\Source\Utility\UI\jucer_JucerTreeViewBase.cpp"/>
@@ -1624,6 +1625,7 @@
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_RelativePath.h"/>
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_TranslationHelpers.h"/>
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_ValueSourceHelpers.h"/>
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_VersionInfo.h"/>
<ClInclude Include="..\..\Source\Utility\PIPs\jucer_PIPGenerator.h"/>
<ClInclude Include="..\..\Source\Utility\UI\PropertyComponents\jucer_ColourPropertyComponent.h"/>
<ClInclude Include="..\..\Source\Utility\UI\PropertyComponents\jucer_FilePathPropertyComponent.h"/>


+ 6
- 0
extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters View File

@@ -496,6 +496,9 @@
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_MiscUtilities.cpp">
<Filter>Projucer\Utility\Helpers</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Utility\Helpers\jucer_VersionInfo.cpp">
<Filter>Projucer\Utility\Helpers</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Utility\PIPs\jucer_PIPGenerator.cpp">
<Filter>Projucer\Utility\PIPs</Filter>
</ClCompile>
@@ -2325,6 +2328,9 @@
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_ValueSourceHelpers.h">
<Filter>Projucer\Utility\Helpers</Filter>
</ClInclude>
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_VersionInfo.h">
<Filter>Projucer\Utility\Helpers</Filter>
</ClInclude>
<ClInclude Include="..\..\Source\Utility\PIPs\jucer_PIPGenerator.h">
<Filter>Projucer\Utility\PIPs</Filter>
</ClInclude>


+ 4
- 0
extras/Projucer/Projucer.jucer View File

@@ -636,6 +636,10 @@
file="Source/Utility/Helpers/jucer_TranslationHelpers.h"/>
<FILE id="EuC4K4" name="jucer_ValueSourceHelpers.h" compile="0" resource="0"
file="Source/Utility/Helpers/jucer_ValueSourceHelpers.h"/>
<FILE id="BPCoKV" name="jucer_VersionInfo.cpp" compile="1" resource="0"
file="Source/Utility/Helpers/jucer_VersionInfo.cpp"/>
<FILE id="TnBQtv" name="jucer_VersionInfo.h" compile="0" resource="0"
file="Source/Utility/Helpers/jucer_VersionInfo.h"/>
</GROUP>
<GROUP id="{A07C4A97-0855-5346-CAF2-A005580B6773}" name="PIPs">
<FILE id="joAnDa" name="jucer_PIPGenerator.cpp" compile="1" resource="0"


+ 121
- 164
extras/Projucer/Source/Application/jucer_AutoUpdater.cpp View File

@@ -52,119 +52,65 @@ void LatestVersionCheckerAndUpdater::checkForNewVersion (bool showAlerts)
//==============================================================================
void LatestVersionCheckerAndUpdater::run()
{
queryUpdateServer();
auto info = VersionInfo::fetchLatestFromUpdateServer();
if (! threadShouldExit())
MessageManager::callAsync ([this] { processResult(); });
}
//==============================================================================
String getOSString()
{
#if JUCE_MAC
return "OSX";
#elif JUCE_WINDOWS
return "Windows";
#elif JUCE_LINUX
return "Linux";
#else
jassertfalse;
return "Unknown";
#endif
}
namespace VersionHelpers
{
String formatProductVersion (int versionNum)
if (info == nullptr)
{
int major = (versionNum & 0xff0000) >> 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<InputStream> 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<DialogWindow> launchDialog (const String& newVersion, const String& releaseNotes)
static std::unique_ptr<DialogWindow> 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<DialogWindow> 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<void()>&& cb)
DownloadAndInstallThread (const VersionInfo::Asset& a, const File& t, std::function<void()>&& 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<InputStream> 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<void()> 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();


+ 5
- 8
extras/Projucer/Source/Application/jucer_AutoUpdater.h View File

@@ -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<DownloadAndInstallThread> installer;
std::unique_ptr<Component> dialogWindow;


+ 44
- 36
extras/Projucer/Source/LiveBuildEngine/jucer_DownloadCompileEngineThread.cpp View File

@@ -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<InputStream> 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();


+ 115
- 0
extras/Projucer/Source/Utility/Helpers/jucer_VersionInfo.cpp View File

@@ -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> VersionInfo::fetchFromUpdateServer (const String& versionString)
{
return fetch ("tags/" + versionString);
}
std::unique_ptr<VersionInfo> VersionInfo::fetchLatestFromUpdateServer()
{
return fetch ("latest");
}
std::unique_ptr<InputStream> VersionInfo::createInputStreamForAsset (const Asset& asset, int& statusCode)
{
URL downloadUrl (asset.url);
StringPairArray responseHeaders;
return std::unique_ptr<InputStream> (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> VersionInfo::fetch (const String& endpoint)
{
URL latestVersionURL ("https://api.github.com/repos/WeAreROLI/JUCE/releases/" + endpoint);
std::unique_ptr<InputStream> 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<VersionInfo::Asset> 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<VersionInfo> (new VersionInfo ({ versionString, releaseNotes, std::move (parsedAssets) }));
}

+ 54
- 0
extras/Projucer/Source/Utility/Helpers/jucer_VersionInfo.h View File

@@ -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<VersionInfo> fetchFromUpdateServer (const String& versionString);
static std::unique_ptr<VersionInfo> fetchLatestFromUpdateServer();
static std::unique_ptr<InputStream> createInputStreamForAsset (const Asset& asset, int& statusCode);
bool isNewerVersionThanCurrent();
const String versionString;
const String releaseNotes;
const std::vector<Asset> assets;
private:
VersionInfo() = default;
static std::unique_ptr<VersionInfo> fetch (const String&);
};

Loading…
Cancel
Save