* Use separate folder for disk cache in debug mode to avoid mixing debug/release-mode object files while testing * Quote the server's file name as it may contain spaces etc. * Fix saving source files during compilation on Windows * Fix JuceDemo for live builds on Mac: long chains of recursive operator<< invocations caused compiler crash * Move code for creating disabled Build tab to extra function and add names to improve readability * Implement new "subscribe" behavior for createDisabledBuildTab * Clean up trailing spacestags/2021-05-28
| @@ -119,7 +119,9 @@ static String getAllSystemInfo() | |||||
| << "User region: " << SystemStats::getUserRegion() << newLine | << "User region: " << SystemStats::getUserRegion() << newLine | ||||
| << "User language: " << SystemStats::getUserLanguage() << newLine | << "User language: " << SystemStats::getUserLanguage() << newLine | ||||
| << "Display language: " << SystemStats::getDisplayLanguage() << newLine | << "Display language: " << SystemStats::getDisplayLanguage() << newLine | ||||
| << newLine | |||||
| << newLine; | |||||
| systemInfo | |||||
| << "Number of CPUs: " << SystemStats::getNumCpus() << newLine | << "Number of CPUs: " << SystemStats::getNumCpus() << newLine | ||||
| << "Memory size: " << SystemStats::getMemorySizeInMegabytes() << " MB" << newLine | << "Memory size: " << SystemStats::getMemorySizeInMegabytes() << " MB" << newLine | ||||
| << "CPU vendor: " << SystemStats::getCpuVendor() << newLine | << "CPU vendor: " << SystemStats::getCpuVendor() << newLine | ||||
| @@ -134,12 +136,16 @@ static String getAllSystemInfo() | |||||
| << "CPU has 3DNOW: " << (SystemStats::has3DNow() ? "yes" : "no") << newLine | << "CPU has 3DNOW: " << (SystemStats::has3DNow() ? "yes" : "no") << newLine | ||||
| << "CPU has AVX: " << (SystemStats::hasAVX() ? "yes" : "no") << newLine | << "CPU has AVX: " << (SystemStats::hasAVX() ? "yes" : "no") << newLine | ||||
| << "CPU has AVX2: " << (SystemStats::hasAVX2() ? "yes" : "no") << newLine | << "CPU has AVX2: " << (SystemStats::hasAVX2() ? "yes" : "no") << newLine | ||||
| << newLine | |||||
| << newLine; | |||||
| systemInfo | |||||
| << "Current working directory: " << File::getCurrentWorkingDirectory().getFullPathName() << newLine | << "Current working directory: " << File::getCurrentWorkingDirectory().getFullPathName() << newLine | ||||
| << "Current application file: " << File::getSpecialLocation (File::currentApplicationFile).getFullPathName() << newLine | << "Current application file: " << File::getSpecialLocation (File::currentApplicationFile).getFullPathName() << newLine | ||||
| << "Current executable file: " << File::getSpecialLocation (File::currentExecutableFile) .getFullPathName() << newLine | << "Current executable file: " << File::getSpecialLocation (File::currentExecutableFile) .getFullPathName() << newLine | ||||
| << "Invoked executable file: " << File::getSpecialLocation (File::invokedExecutableFile) .getFullPathName() << newLine | << "Invoked executable file: " << File::getSpecialLocation (File::invokedExecutableFile) .getFullPathName() << newLine | ||||
| << newLine | |||||
| << newLine; | |||||
| systemInfo | |||||
| << "User home folder: " << File::getSpecialLocation (File::userHomeDirectory) .getFullPathName() << newLine | << "User home folder: " << File::getSpecialLocation (File::userHomeDirectory) .getFullPathName() << newLine | ||||
| << "User desktop folder: " << File::getSpecialLocation (File::userDesktopDirectory) .getFullPathName() << newLine | << "User desktop folder: " << File::getSpecialLocation (File::userDesktopDirectory) .getFullPathName() << newLine | ||||
| << "User documents folder: " << File::getSpecialLocation (File::userDocumentsDirectory) .getFullPathName() << newLine | << "User documents folder: " << File::getSpecialLocation (File::userDocumentsDirectory) .getFullPathName() << newLine | ||||
| @@ -150,7 +156,9 @@ static String getAllSystemInfo() | |||||
| << "Common application data folder: " << File::getSpecialLocation (File::commonApplicationDataDirectory).getFullPathName() << newLine | << "Common application data folder: " << File::getSpecialLocation (File::commonApplicationDataDirectory).getFullPathName() << newLine | ||||
| << "Common documents folder: " << File::getSpecialLocation (File::commonDocumentsDirectory) .getFullPathName() << newLine | << "Common documents folder: " << File::getSpecialLocation (File::commonDocumentsDirectory) .getFullPathName() << newLine | ||||
| << "Local temp folder: " << File::getSpecialLocation (File::tempDirectory) .getFullPathName() << newLine | << "Local temp folder: " << File::getSpecialLocation (File::tempDirectory) .getFullPathName() << newLine | ||||
| << newLine | |||||
| << newLine; | |||||
| systemInfo | |||||
| << "File System roots: " << getFileSystemRoots() << newLine | << "File System roots: " << getFileSystemRoots() << newLine | ||||
| << "Free space in home folder: " << File::descriptionOfSizeInBytes (File::getSpecialLocation (File::userHomeDirectory) | << "Free space in home folder: " << File::descriptionOfSizeInBytes (File::getSpecialLocation (File::userHomeDirectory) | ||||
| .getBytesFreeOnVolume()) << newLine | .getBytesFreeOnVolume()) << newLine | ||||
| @@ -89,9 +89,15 @@ namespace ProjectProperties | |||||
| static File getCacheLocation (Project& project) | static File getCacheLocation (Project& project) | ||||
| { | { | ||||
| String cacheFolderName = project.getProjectFilenameRoot() + "_" + project.getProjectUID(); | |||||
| #if JUCE_DEBUG | |||||
| cacheFolderName += "_debug"; | |||||
| #endif | |||||
| return getProjucerTempFolder() | return getProjucerTempFolder() | ||||
| .getChildFile ("Intermediate Files") | .getChildFile ("Intermediate Files") | ||||
| .getChildFile (project.getProjectFilenameRoot() + "_" + project.getProjectUID()); | |||||
| .getChildFile (cacheFolderName); | |||||
| } | } | ||||
| } | } | ||||
| @@ -230,7 +230,7 @@ String createCommandLineForLaunchingServer (const String& pipeName, const String | |||||
| const File exe (File::getSpecialLocation (File::currentExecutableFile).getFullPathName()); | const File exe (File::getSpecialLocation (File::currentExecutableFile).getFullPathName()); | ||||
| return exe.getFullPathName() + " " + commandPrefix + info.joinIntoString (commandTokenSeparator); | |||||
| return "\"" + exe.getFullPathName() + "\" " + commandPrefix + info.joinIntoString (commandTokenSeparator); | |||||
| } | } | ||||
| static ServerIPC* currentServer = nullptr; | static ServerIPC* currentServer = nullptr; | ||||
| @@ -354,65 +354,95 @@ struct BuildTabComponent : public ConcertinaPanel | |||||
| struct ProjucerDisabledComp : public Component, | struct ProjucerDisabledComp : public Component, | ||||
| private Button::Listener | private Button::Listener | ||||
| { | { | ||||
| ProjucerDisabledComp (String message, bool loggedIn, bool canLogin, bool requirePurchase = false, | |||||
| const String& loginName = String()) | |||||
| : isLoggedIn (loggedIn), isPurchaseButton (requirePurchase) | |||||
| ProjucerDisabledComp (String message, bool loggedIn, bool showSubscribeButton, | |||||
| bool showSignInButton, bool showSwitchAccountButton) | |||||
| : isLoggedIn (loggedIn) | |||||
| { | { | ||||
| infoLabel.setColour (Label::textColourId, findColour (mainBackgroundColourId).contrasting (0.7f)); | infoLabel.setColour (Label::textColourId, findColour (mainBackgroundColourId).contrasting (0.7f)); | ||||
| infoLabel.setJustificationType (Justification::centred); | infoLabel.setJustificationType (Justification::centred); | ||||
| infoLabel.setText (message, dontSendNotification); | infoLabel.setText (message, dontSendNotification); | ||||
| addAndMakeVisible (infoLabel); | addAndMakeVisible (infoLabel); | ||||
| if (canLogin) | |||||
| if (showSubscribeButton) | |||||
| { | { | ||||
| addAndMakeVisible (loginButton); | |||||
| loginButton.addListener (this); | |||||
| subscribeButton = new TextButton (String ( "Subscribe...")); | |||||
| addAndMakeVisible (*subscribeButton); | |||||
| subscribeButton->addListener (this); | |||||
| } | |||||
| if (isPurchaseButton) | |||||
| { | |||||
| loginButton.setButtonText ("Purchase JUCE Pro..."); | |||||
| signOutButton = new TextButton (String ("Sign Out ") + loginName); | |||||
| addAndMakeVisible (*signOutButton); | |||||
| signOutButton->addListener (this); | |||||
| } | |||||
| if (showSignInButton) | |||||
| { | |||||
| signInButton = new TextButton (String ( "Sign in...")); | |||||
| addAndMakeVisible (*signInButton); | |||||
| signInButton->addListener (this); | |||||
| } | |||||
| if (showSwitchAccountButton) | |||||
| { | |||||
| switchAccountButton = new TextButton (String ("Switch account...")); | |||||
| addAndMakeVisible (*switchAccountButton); | |||||
| switchAccountButton->addListener (this); | |||||
| } | } | ||||
| } | } | ||||
| void resized() override | void resized() override | ||||
| { | { | ||||
| infoLabel.centreWithSize (proportionOfWidth (0.9f), 200); | |||||
| loginButton.setSize (jmin (getWidth() - 10, 150), 22); | |||||
| loginButton.setCentrePosition (infoLabel.getBounds().getCentreX(), | |||||
| infoLabel.getBottom() + loginButton.getHeight() * 2); | |||||
| int infoWidth = proportionOfWidth (0.9f); | |||||
| int infoHeight = 100; | |||||
| infoLabel.centreWithSize (infoWidth, infoHeight); | |||||
| if (signOutButton != nullptr) | |||||
| int buttonWidth = jmin (getWidth() - 10, 150); | |||||
| int buttonHeight = 22; | |||||
| int itemDistance = 10; | |||||
| int buttonCenterX = infoLabel.getBounds().getCentreX(); | |||||
| int buttonCenterY = infoLabel.getBottom() + itemDistance + buttonHeight / 2; | |||||
| if (subscribeButton.get() != nullptr) | |||||
| { | { | ||||
| signOutButton->setSize (jmin (getWidth() - 10, 150), 22); | |||||
| signOutButton->setCentrePosition (infoLabel.getBounds().getCentreX(), | |||||
| loginButton.getBottom() + 20); | |||||
| subscribeButton->setSize (buttonWidth, buttonHeight); | |||||
| subscribeButton->setCentrePosition (buttonCenterX, buttonCenterY); | |||||
| buttonCenterY += itemDistance + buttonHeight; | |||||
| } | |||||
| if (signInButton.get() != nullptr) | |||||
| { | |||||
| signInButton->setSize (buttonWidth, buttonHeight); | |||||
| signInButton->setCentrePosition (buttonCenterX, buttonCenterY); | |||||
| buttonCenterY += itemDistance + buttonHeight; | |||||
| } | |||||
| if (switchAccountButton.get() != nullptr) | |||||
| { | |||||
| switchAccountButton->setSize (buttonWidth, buttonHeight); | |||||
| switchAccountButton->setCentrePosition (buttonCenterX, buttonCenterY); | |||||
| } | } | ||||
| } | } | ||||
| void buttonClicked (Button* btn) override | void buttonClicked (Button* btn) override | ||||
| { | { | ||||
| if (btn == &loginButton) | |||||
| if (btn == subscribeButton.get()) | |||||
| { | |||||
| URL ("http://www.juce.com/get-juce").launchInDefaultBrowser(); | |||||
| } | |||||
| else if (btn == signInButton.get()) | |||||
| { | { | ||||
| if (isPurchaseButton) | |||||
| URL ("http://www.juce.com").launchInDefaultBrowser(); | |||||
| else | |||||
| ProjucerApplication::getApp().showLoginForm(); | |||||
| ProjucerApplication::getApp().showLoginForm(); | |||||
| } | } | ||||
| else if (btn == signOutButton.get()) | |||||
| else if (btn == switchAccountButton.get()) | |||||
| { | { | ||||
| ProjucerLicenses::getInstance()->logout(); | |||||
| ProjucerApplication::getApp().updateAllBuildTabs(); | |||||
| ProjucerApplication::getApp().showLoginForm(); | |||||
| } | } | ||||
| } | } | ||||
| bool isLoggedIn; | |||||
| private: | |||||
| Label infoLabel { "info", String() }; | Label infoLabel { "info", String() }; | ||||
| TextButton loginButton { "Log-in..." }; | |||||
| ScopedPointer<TextButton> signOutButton; | |||||
| bool isLoggedIn, isPurchaseButton; | |||||
| ScopedPointer<TextButton> subscribeButton; | |||||
| ScopedPointer<TextButton> signInButton; | |||||
| ScopedPointer<TextButton> switchAccountButton; | |||||
| }; | }; | ||||
| struct EnableBuildComp : public Component | struct EnableBuildComp : public Component | ||||
| @@ -458,33 +488,30 @@ Component* ProjectContentComponent::createBuildTab (CompileEngineChildProcess* c | |||||
| return new BuildTabComponent (child, new ProjucerAppClasses::ErrorListComp (child->errorList)); | return new BuildTabComponent (child, new ProjucerAppClasses::ErrorListComp (child->errorList)); | ||||
| } | } | ||||
| auto& unlockStatus = *ProjucerLicenses::getInstance(); | |||||
| if (unlockStatus.hasLiveCodingLicence() | |||||
| && project != nullptr | |||||
| && LiveBuildProjectSettings::isBuildDisabled (*project)) | |||||
| return new EnableBuildComp(); | |||||
| if (unlockStatus.isLoggedIn()) | |||||
| return new ProjucerDisabledComp (String ("The Projucer's live-build features are currently disabled!") + newLine | |||||
| + newLine | |||||
| + "Your account " + unlockStatus.getLoginName().quoted() | |||||
| + " does not have an asscociated JUCE Pro license:", | |||||
| true, true, true, unlockStatus.getLoginName()); | |||||
| if (! unlockStatus.isDLLPresent()) | |||||
| return new ProjucerDisabledComp (String ("The live-building DLL is missing!") + newLine | |||||
| + newLine | |||||
| + "To enable the compiler, you'll need to install the missing DLL " | |||||
| + CompileEngineDLL::getDLLName().quoted() + newLine | |||||
| + newLine | |||||
| + "Visit the JUCE website/forum for more help on getting and installing the DLL!", | |||||
| false, false); | |||||
| return new ProjucerDisabledComp ("The Projucer's live-build features are currently disabled!\n\n" | |||||
| "To enable them, you'll need to log-in with your JUCE account details:", | |||||
| false, true, false); | |||||
| jassert (project != nullptr); | |||||
| const auto& unlockStatus = *ProjucerLicenses::getInstance(); | |||||
| if (unlockStatus.hasLiveCodingLicence()) | |||||
| { | |||||
| jassert (unlockStatus.isLoggedIn()); | |||||
| jassert (unlockStatus.isDLLPresent()); | |||||
| return new EnableBuildComp(); | |||||
| } | |||||
| return createDisabledBuildTab(unlockStatus.isLoggedIn(), | |||||
| unlockStatus.isDLLPresent()); | |||||
| #endif | #endif | ||||
| }; | |||||
| //============================================================================== | |||||
| Component* ProjectContentComponent::createDisabledBuildTab(bool loggedIn, bool dllPresent) { | |||||
| bool showSubscribeButton = true; | |||||
| bool showSignInButton = dllPresent && ! loggedIn; | |||||
| bool showSwitchAccountButton = dllPresent && loggedIn; | |||||
| return new ProjucerDisabledComp ( | |||||
| "Subscribe to JUCE Pro or Indie to use the Projucer's live-build features:", | |||||
| loggedIn, showSubscribeButton, showSignInButton, showSwitchAccountButton); | |||||
| } | } | ||||
| BuildTabComponent* findBuildTab (const TabbedComponent& tabs) | BuildTabComponent* findBuildTab (const TabbedComponent& tabs) | ||||
| @@ -1499,7 +1526,8 @@ void ProjectContentComponent::handleMissingSystemHeaders() | |||||
| deleteProjectTabs(); | deleteProjectTabs(); | ||||
| createProjectTabs(); | createProjectTabs(); | ||||
| ProjucerDisabledComp* buildTab = new ProjucerDisabledComp (tabMessage, false, false); | |||||
| bool isLoggedIn = ProjucerLicenses::getInstance()->isLoggedIn(); | |||||
| ProjucerDisabledComp* buildTab = new ProjucerDisabledComp (tabMessage, isLoggedIn, false, false, false); | |||||
| treeViewTabs.addTab ("Build", Colours::transparentBlack, buildTab, true); | treeViewTabs.addTab ("Build", Colours::transparentBlack, buildTab, true); | ||||
| showBuildTab(); | showBuildTab(); | ||||
| @@ -154,6 +154,8 @@ private: | |||||
| void timerCallback() override; | void timerCallback() override; | ||||
| Component* createBuildTab (CompileEngineChildProcess*); | Component* createBuildTab (CompileEngineChildProcess*); | ||||
| Component* createDisabledBuildTab (bool loggedIn, bool dllPresent); | |||||
| bool isContinuousRebuildEnabled() { return getAppSettings().getGlobalProperties().getBoolValue ("continuousRebuild", true); } | bool isContinuousRebuildEnabled() { return getAppSettings().getGlobalProperties().getBoolValue ("continuousRebuild", true); } | ||||
| void setContinuousRebuildEnabled (bool b) { getAppSettings().getGlobalProperties().setValue ("continuousRebuild", b); } | void setContinuousRebuildEnabled (bool b) { getAppSettings().getGlobalProperties().setValue ("continuousRebuild", b); } | ||||
| void rebuildNow(); | void rebuildNow(); | ||||
| @@ -300,6 +300,21 @@ bool File::copyFileTo (const File& newFile) const | |||||
| || (exists() && newFile.deleteFile() && copyInternal (newFile)); | || (exists() && newFile.deleteFile() && copyInternal (newFile)); | ||||
| } | } | ||||
| bool File::replaceFileIn (const File& newFile) const | |||||
| { | |||||
| if (newFile.fullPath == fullPath) | |||||
| return true; | |||||
| if (! newFile.exists()) | |||||
| return moveFileTo (newFile); | |||||
| if (! replaceInternal (newFile)) | |||||
| return false; | |||||
| deleteFile(); | |||||
| return true; | |||||
| } | |||||
| bool File::copyDirectoryTo (const File& newDirectory) const | bool File::copyDirectoryTo (const File& newDirectory) const | ||||
| { | { | ||||
| if (isDirectory() && newDirectory.createDirectory()) | if (isDirectory() && newDirectory.createDirectory()) | ||||
| @@ -503,6 +503,18 @@ public: | |||||
| */ | */ | ||||
| bool copyFileTo (const File& targetLocation) const; | bool copyFileTo (const File& targetLocation) const; | ||||
| /** Replaces a file. | |||||
| Replace the file in the given location, assuming the replaced files identity. | |||||
| Depending on the file system this will preserve file attributes such as | |||||
| creation date, short file name, etc. | |||||
| If replacement succeeds the original file is deleted. | |||||
| @returns true if the operation succeeds | |||||
| */ | |||||
| bool replaceFileIn (const File& targetLocation) const; | |||||
| /** Copies a directory. | /** Copies a directory. | ||||
| Tries to copy an entire directory, recursively. | Tries to copy an entire directory, recursively. | ||||
| @@ -982,6 +994,7 @@ private: | |||||
| Result createDirectoryInternal (const String&) const; | Result createDirectoryInternal (const String&) const; | ||||
| bool copyInternal (const File&) const; | bool copyInternal (const File&) const; | ||||
| bool moveInternal (const File&) const; | bool moveInternal (const File&) const; | ||||
| bool replaceInternal (const File&) const; | |||||
| bool setFileTimesInternal (int64 m, int64 a, int64 c) const; | bool setFileTimesInternal (int64 m, int64 a, int64 c) const; | ||||
| void getFileTimesInternal (int64& m, int64& a, int64& c) const; | void getFileTimesInternal (int64& m, int64& a, int64& c) const; | ||||
| bool setFileReadOnlyInternal (bool) const; | bool setFileReadOnlyInternal (bool) const; | ||||
| @@ -86,7 +86,7 @@ bool TemporaryFile::overwriteTargetFileWithTemporary() const | |||||
| // Have a few attempts at overwriting the file before giving up.. | // Have a few attempts at overwriting the file before giving up.. | ||||
| for (int i = 5; --i >= 0;) | for (int i = 5; --i >= 0;) | ||||
| { | { | ||||
| if (temporaryFile.moveFileTo (targetFile)) | |||||
| if (temporaryFile.replaceFileIn (targetFile)) | |||||
| return true; | return true; | ||||
| Thread::sleep (100); | Thread::sleep (100); | ||||
| @@ -440,6 +440,11 @@ bool File::moveInternal (const File& dest) const | |||||
| return false; | return false; | ||||
| } | } | ||||
| bool File::replaceInternal (const File& dest) const | |||||
| { | |||||
| return moveInternal (dest); | |||||
| } | |||||
| Result File::createDirectoryInternal (const String& fileName) const | Result File::createDirectoryInternal (const String& fileName) const | ||||
| { | { | ||||
| return getResultForReturnValue (mkdir (fileName.toUTF8(), 0777)); | return getResultForReturnValue (mkdir (fileName.toUTF8(), 0777)); | ||||
| @@ -220,6 +220,15 @@ bool File::moveInternal (const File& dest) const | |||||
| return MoveFile (fullPath.toWideCharPointer(), dest.getFullPathName().toWideCharPointer()) != 0; | return MoveFile (fullPath.toWideCharPointer(), dest.getFullPathName().toWideCharPointer()) != 0; | ||||
| } | } | ||||
| bool File::replaceInternal (const File& dest) const | |||||
| { | |||||
| void* lpExclude = 0; | |||||
| void* lpReserved = 0; | |||||
| return ReplaceFile (dest.getFullPathName().toWideCharPointer(), fullPath.toWideCharPointer(), | |||||
| 0, REPLACEFILE_IGNORE_MERGE_ERRORS, lpExclude, lpReserved) != 0; | |||||
| } | |||||
| Result File::createDirectoryInternal (const String& fileName) const | Result File::createDirectoryInternal (const String& fileName) const | ||||
| { | { | ||||
| return CreateDirectory (fileName.toWideCharPointer(), 0) ? Result::ok() | return CreateDirectory (fileName.toWideCharPointer(), 0) ? Result::ok() | ||||