1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-02-04 03:40:07 +00:00

Projucer: Removed the live build

This commit is contained in:
Tom Poole 2021-05-14 08:18:19 +01:00
parent 64896eefcd
commit bfdda737a2
74 changed files with 673 additions and 7583 deletions

View file

@ -1,358 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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 ErrorListComp : public TreePanelBase,
private ChangeListener
{
public:
ErrorListComp (ErrorList& el)
: TreePanelBase (nullptr, String()),
errorList (el)
{
setName ("Errors and Warnings");
setEmptyTreeMessage ("(No Messages)");
tree.setMultiSelectEnabled (false);
tree.setRootItemVisible (false);
setRoot (new ErrorRootTreeItem (errorList));
errorList.addChangeListener (this);
errorListChanged();
}
~ErrorListComp() override
{
errorList.removeChangeListener (this);
}
void errorListChanged()
{
static_cast<ErrorRootTreeItem*> (rootItem.get())->refreshSubItems();
}
void moveBy (const int delta)
{
if (delta < 0)
if (TreeViewItem* selected = tree.getSelectedItem (0))
if (selected->getRowNumberInTree() <= 1)
return;
tree.moveSelectedRow (delta);
if (dynamic_cast<ErrorMessageTreeItem*> (tree.getSelectedItem (0)) == nullptr)
tree.moveSelectedRow (delta);
}
void showNext() { moveBy (1); }
void showPrevious() { moveBy (-1); }
private:
TreeView list;
ErrorList& errorList;
struct ErrorMessageTreeItem;
void changeListenerCallback (ChangeBroadcaster*) override
{
errorListChanged();
}
static void limitNumberOfSubItems (TreeViewItem& item, const int maxSubItems)
{
while (item.getNumSubItems() > maxSubItems)
item.removeSubItem (item.getNumSubItems() - 1);
}
//==============================================================================
class ErrorRootTreeItem : public JucerTreeViewBase
{
public:
ErrorRootTreeItem (ErrorList& el) : errorList (el) {}
String getRenamingName() const override { return getDisplayName(); }
String getDisplayName() const override { return "Errors and Warnings"; }
void setName (const String&) override {}
bool isMissing() const override { return false; }
Icon getIcon() const override { return Icon (getIcons().bug, getContentColour (true)); }
bool canBeSelected() const override { return true; }
bool mightContainSubItems() override { return true; }
String getUniqueName() const override { return "errors"; }
void refreshSubItems()
{
Array<DiagnosticMessage> errors;
errorList.takeCopy (errors);
StringArray files;
for (const auto& m : errors)
{
files.addIfNotAlreadyThere (m.mainFile);
if (m.associatedDiagnostic != nullptr)
files.addIfNotAlreadyThere (m.associatedDiagnostic->mainFile);
}
limitNumberOfSubItems (*this, files.size());
int i = 0;
for (const auto& f : files)
{
if (i >= getNumSubItems() || static_cast<CompileUnitTreeItem*> (getSubItem (i))->compileUnit != f)
{
limitNumberOfSubItems (*this, i);
addSubItem (new CompileUnitTreeItem (f));
}
static_cast<CompileUnitTreeItem*> (getSubItem (i))->refresh (errors);
++i;
}
}
private:
ErrorList& errorList;
};
//==============================================================================
struct CompileUnitTreeItem : public JucerTreeViewBase
{
CompileUnitTreeItem (const String& filename) : compileUnit (filename) {}
void setName (const String&) override {}
void addSubItems() override {}
bool isMissing() const override { return false; }
Icon getIcon() const override { return Icon (getIcons().bug, getContentColour (true)); }
bool canBeSelected() const override { return true; }
bool mightContainSubItems() override { return true; }
String getUniqueName() const override { return String::toHexString (compileUnit.hashCode64()); }
String getRenamingName() const override { return getDisplayName(); }
String getDisplayName() const override
{
if (File::isAbsolutePath (compileUnit))
{
File f (compileUnit);
return f.exists() ? f.getFileName() : compileUnit;
}
if (! compileUnit.isEmpty())
return compileUnit;
return String ("Global");
}
void showOverlays()
{
for (int i = 0; i < getNumSubItems(); ++i)
if (auto* e = dynamic_cast<ErrorMessageTreeItem*> (getSubItem (i)))
e->showOverlays();
}
ErrorMessageTreeItem* getItemForError (const DiagnosticMessage& m) const
{
for (int i = 0; i < getNumSubItems(); ++i)
if (auto* item = dynamic_cast<ErrorMessageTreeItem*> (getSubItem(i)))
if (item->message == m)
return item;
return nullptr;
}
void refresh (const Array<DiagnosticMessage>& allErrors)
{
clearSubItems();
for (const auto& error : allErrors)
if (error.mainFile == compileUnit && error.associatedDiagnostic == nullptr)
addSubItem (new ErrorMessageTreeItem (error));
for (const auto& error : allErrors)
if (error.mainFile == compileUnit && error.associatedDiagnostic != nullptr)
if (ErrorMessageTreeItem* parent = getItemForError (*error.associatedDiagnostic))
parent->addSubItem (new ErrorMessageTreeItem (error));
}
void showDocument() override
{
if (ProjectContentComponent* pcc = getProjectContentComponent())
if (File::isAbsolutePath (compileUnit) && File (compileUnit).exists())
pcc->showEditorForFile (File (compileUnit), true);
}
String compileUnit;
};
//==============================================================================
struct ErrorMessageTreeItem : public JucerTreeViewBase
{
ErrorMessageTreeItem (const DiagnosticMessage& m)
: message (m), itemHeight (25)
{
setOpenness (Openness::opennessClosed);
uniqueID << message.message << ':' << message.range.toString();
}
~ErrorMessageTreeItem() override
{
overlay.deleteAndZero();
}
String getRenamingName() const override { return getDisplayName(); }
String getDisplayName() const override { return message.message; }
void setName (const String&) override {}
bool isMissing() const override { return false; }
Icon getIcon() const override { return Icon (message.isNote() ? getIcons().info
: getIcons().warning, getContentColour (true)); }
bool canBeSelected() const override { return true; }
bool mightContainSubItems() override { return getNumSubItems() != 0; }
String getUniqueName() const override { return uniqueID; }
void paintContent (Graphics& g, Rectangle<int> area) override
{
jassert (area.getWidth() >= 0);
AttributedString s (message.message);
s.setFont (Font (12.0f));
s.setColour (getContentColour (false));
s.setJustification (Justification::centredLeft);
text.createLayout (s, (float) area.getWidth());
const auto newHeight = 2 + jmax (25, (int) text.getHeight());
if (itemHeight != newHeight)
{
itemHeight = newHeight;
treeHasChanged();
}
text.draw (g, area.toFloat());
}
Colour getContentColour (bool isIcon) const override
{
if (isIcon)
{
if (isSelected())
return getOwnerView()->findColour (defaultHighlightedTextColourId);
if (message.isError())
return Colours::red;
if (message.isWarning())
return Colours::yellow;
return getOwnerView()->findColour (treeIconColourId);
}
return getOwnerView()->findColour (isSelected() ? defaultHighlightedTextColourId
: defaultTextColourId);
}
void showPopupMenu (Point<int> p) override
{
PopupMenu menu;
menu.addItem (1, "Copy");
launchPopupMenu (menu, p);
}
void handlePopupMenuResult (int resultCode) override
{
if (resultCode == 1)
SystemClipboard::copyTextToClipboard (message.toString());
}
int getItemHeight() const override
{
return itemHeight;
}
SourceCodeEditor* getEditor()
{
if (auto* pcc = getProjectContentComponent())
{
const auto file = File::createFileWithoutCheckingPath (message.range.file);
if (message.range.isValid() && file.exists() && pcc->showEditorForFile (file, false))
return dynamic_cast<SourceCodeEditor*> (pcc->getEditorComponent());
}
return nullptr;
}
void showDocument() override
{
if (SourceCodeEditor* ed = getEditor())
{
ed->grabKeyboardFocus();
ed->highlight (message.range.range, false);
if (auto cu = findCompileUnitParent())
cu->showOverlays();
}
}
CompileUnitTreeItem* findCompileUnitParent()
{
for (TreeViewItem* p = getParentItem(); p != nullptr; p = p->getParentItem())
if (auto cu = dynamic_cast<CompileUnitTreeItem*> (p))
return cu;
return nullptr;
}
void showOverlays()
{
overlay.deleteAndZero();
if (ProjectContentComponent* pcc = getProjectContentComponent())
{
if (auto* ed = dynamic_cast<SourceCodeEditor*> (pcc->getEditorComponent()))
{
auto start = CodeDocument::Position (ed->editor->getDocument(), message.range.range.getStart());
auto end = CodeDocument::Position (ed->editor->getDocument(), message.range.range.getEnd());
if (auto* ce = dynamic_cast<LiveBuildCodeEditor*> (ed->editor.get()))
overlay = ce->addDiagnosticOverlay (start, end, message.type);
}
}
for (int i = 0; i < getNumSubItems(); ++i)
if (auto* e = dynamic_cast<ErrorMessageTreeItem*> (getSubItem (i)))
e->showOverlays();
}
DiagnosticMessage message;
private:
String uniqueID;
TextLayout text;
int itemHeight;
Component::SafePointer<Component> overlay;
};
};

View file

@ -1,111 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
//==============================================================================
struct ActivityList : public ChangeBroadcaster
{
ActivityList() {}
void setList (const StringArray& newList)
{
checkThread();
if (activities != newList)
{
const bool wasEmpty = isEmpty();
activities = newList;
sendChangeMessage();
if (wasEmpty != isEmpty())
ProjucerApplication::getCommandManager().commandStatusChanged();
}
}
void clear()
{
setList (StringArray());
}
StringArray getActivities() const
{
checkThread();
StringArray s;
for (auto a : activities)
s.add (a.upToFirstOccurrenceOf ("|||", false, false));
return s;
}
bool isEmpty() const noexcept
{
return activities.size() == 0;
}
int getNumActivities() const
{
checkThread();
return activities.size();
}
struct Listener
{
virtual ~Listener() {}
virtual void classListChanged (const ClassDatabase::ClassList&) = 0;
};
void addListener (Listener* l)
{
checkThread();
listeners.add (l);
}
void removeListener (Listener* l)
{
checkThread();
listeners.remove (l);
}
void sendClassListChangedMessage (const ClassDatabase::ClassList& newList)
{
checkThread();
listeners.call ([&] (Listener& l) { l.classListChanged (newList); });
}
private:
StringArray activities;
ListenerList<Listener> listeners;
static void checkThread()
{
JUCE_ASSERT_MESSAGE_THREAD
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActivityList)
};

View file

@ -1,731 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
//==============================================================================
struct ClassDatabase
{
//==============================================================================
struct MemberInfo
{
enum CodeLocationType
{
declaration = 0,
addedToParent,
setBoundsParamX,
setBoundsParamY,
setBoundsParamW,
setBoundsParamH,
// WARNING! When you change any of these, also update the copy that lives in the live editing code
numCodeLocationTypes
};
MemberInfo() {}
MemberInfo (const MemberInfo& other)
: name (other.name), type (other.type)
{
for (int i = 0; i < numCodeLocationTypes; ++i)
locations[i] = other.locations[i];
}
MemberInfo (const String& nm, const String& ty)
: name (nm), type (ty)
{
}
MemberInfo (const ValueTree& v)
: name (v [Ids::name].toString()),
type (v [Ids::class_].toString())
{
for (int i = 0; i < numCodeLocationTypes; ++i)
locations[i] = v [getIdentifierForCodeLocationType (i)].toString();
}
const String& getName() const { return name; }
const String& getType() const { return type; }
const SourceCodeRange& getLocation (CodeLocationType t) const
{
return locations[t];
}
void setLocation (CodeLocationType t, const SourceCodeRange& range)
{
locations[t] = range;
}
void mergeWith (const MemberInfo& other)
{
jassert (name == other.name);
if (other.type.isNotEmpty())
type = other.type;
for (int i = 0; i < numCodeLocationTypes; ++i)
if (other.locations[i].isValid())
locations[i] = other.locations[i];
}
void nudgeAllCodeRanges (const String& file, const int insertPoint, const int delta)
{
for (int i = 0; i < numCodeLocationTypes; ++i)
locations[i].nudge (file, insertPoint, delta);
}
void fileContentChanged (const String& file)
{
for (int i = 0; i < numCodeLocationTypes; ++i)
locations[i].fileContentChanged (file);
}
ValueTree toValueTree() const
{
ValueTree m (Ids::MEMBER);
m.setProperty (Ids::name, name, nullptr);
m.setProperty (Ids::class_, type, nullptr);
for (int i = 0; i < numCodeLocationTypes; ++i)
locations[i].writeToValueTree (m, getIdentifierForCodeLocationType (i));
return m;
}
private:
String name, type;
SourceCodeRange locations [numCodeLocationTypes];
static Identifier getIdentifierForCodeLocationType (int typeIndex)
{
// (These need to remain in order)
static_assert (setBoundsParamX + 1 == setBoundsParamY && setBoundsParamY + 1 == setBoundsParamW
&& setBoundsParamW + 1 == setBoundsParamH, "");
static const Identifier ids[] =
{
"declaration",
"addedToParent",
"setBoundsParamX",
"setBoundsParamY",
"setBoundsParamW",
"setBoundsParamH"
};
return ids [typeIndex];
}
};
//==============================================================================
struct MethodInfo
{
MethodInfo() {}
MethodInfo (const MethodInfo& other)
: name (other.name), returnType (other.returnType),
declaration (other.declaration), definition (other.definition),
numArgs (other.numArgs), flags (other.flags)
{
}
String name, returnType;
SourceCodeRange declaration, definition;
int numArgs, flags;
enum
{
isConstructor = 1,
isDefaultConstructor = 2,
isTemplated = 4,
isPublic = 8
};
MethodInfo (const ValueTree& v)
: name (v[Ids::name].toString()),
returnType (v[Ids::returnType].toString()),
declaration (v[Ids::declaration].toString()),
definition (v[Ids::definition].toString()),
numArgs (v[Ids::numArgs]),
flags (v[Ids::flags])
{
}
ValueTree toValueTree() const
{
ValueTree m (Ids::METHOD);
m.setProperty (Ids::name, name, nullptr);
m.setProperty (Ids::returnType, returnType, nullptr);
m.setProperty (Ids::numArgs, numArgs, nullptr);
m.setProperty (Ids::flags, flags, nullptr);
declaration.writeToValueTree (m, Ids::declaration);
definition.writeToValueTree (m, Ids::definition);
return m;
}
void nudgeAllCodeRanges (const String& file, const int insertPoint, const int delta)
{
declaration.nudge (file, insertPoint, delta);
definition.nudge (file, insertPoint, delta);
}
void fileContentChanged (const String& file)
{
declaration.fileContentChanged (file);
definition.fileContentChanged (file);
}
};
//==============================================================================
struct InstantiationFlags
{
InstantiationFlags()
: isAbstract (false),
inAnonymousNamespace (false),
noDefaultConstructor (false)
{}
bool canBeInstantiated() const noexcept
{
return ! (isAbstract || inAnonymousNamespace || noDefaultConstructor);
}
String getReasonForUnavailability() const
{
if (isAbstract) return "This class is abstract";
if (noDefaultConstructor) return "This class has no default constructor";
if (inAnonymousNamespace) return "This class is declared inside an anonymous namespace";
return String();
}
bool isDisallowed (const InstantiationFlags& disallowedFlags) const
{
return ! ((disallowedFlags.isAbstract && isAbstract)
|| (disallowedFlags.inAnonymousNamespace && inAnonymousNamespace)
|| (disallowedFlags.noDefaultConstructor && noDefaultConstructor));
}
bool isAbstract;
bool inAnonymousNamespace;
bool noDefaultConstructor;
};
//==============================================================================
struct Class
{
Class() {}
~Class() {}
Class (const Class& other)
: className (other.className), members (other.members),
methods (other.methods), classDeclaration (other.classDeclaration),
instantiationFlags (other.instantiationFlags)
{
}
Class (const String& name, const InstantiationFlags& flags,
const Array<MemberInfo>& m,
const Array<MethodInfo>& meth,
const SourceCodeRange& classDeclarationRange)
: className (name),
members (m), methods (meth),
classDeclaration (classDeclarationRange),
instantiationFlags (flags)
{
}
Class& operator= (const Class& other)
{
className = other.className;
members = other.members;
methods = other.methods;
classDeclaration = other.classDeclaration;
instantiationFlags = other.instantiationFlags;
return *this;
}
const String& getName() const noexcept { return className; }
const InstantiationFlags& getInstantiationFlags() const
{
return instantiationFlags;
}
void setInstantiationFlags (const InstantiationFlags& newFlags)
{
instantiationFlags = newFlags;
}
const SourceCodeRange& getClassDeclarationRange() const
{
return classDeclaration;
}
const MemberInfo* findMember (const String& memberName) const
{
for (auto& m : members)
if (m.getName() == memberName)
return &m;
return nullptr;
}
MemberInfo* findMember (const String& memberName)
{
return const_cast<MemberInfo*> (static_cast<const Class&>(*this).findMember (memberName));
}
const MethodInfo* getDefaultConstructor() const
{
for (const MethodInfo& m : methods)
if ((m.flags & MethodInfo::isDefaultConstructor) != 0)
return &m;
return nullptr;
}
const MethodInfo* getConstructor() const
{
if (const MethodInfo* m = getDefaultConstructor())
return m;
for (const MethodInfo& m : methods)
if ((m.flags & MethodInfo::isConstructor) != 0)
return &m;
return nullptr;
}
const MethodInfo* getResizedMethod() const
{
for (const MethodInfo& m : methods)
if (m.name == "resized" && m.numArgs == 0)
return &m;
return nullptr;
}
File getMainSourceFile() const
{
if (const MethodInfo* m = getResizedMethod())
if (m->definition.isValid())
return m->definition.file;
if (const MethodInfo* m = getConstructor())
if (m->definition.isValid())
return m->definition.file;
for (auto& m : methods)
if (m.definition.isValid() && File (m.definition.file).hasFileExtension ("cpp;mm"))
return m.definition.file;
for (auto& m : methods)
if ((m.flags & MethodInfo::isConstructor) != 0 && m.definition.isValid())
return m.definition.file;
for (auto& m : methods)
if (m.definition.isValid() && File (m.definition.file).exists())
return m.definition.file;
return {};
}
Array<File> getAllSourceFiles() const
{
Array<File> files;
for (const MethodInfo& m : methods)
{
files.addIfNotAlreadyThere (m.declaration.file);
files.addIfNotAlreadyThere (m.definition.file);
}
return files;
}
bool isDeclaredInFile (const File& file) const
{
return file == classDeclaration.file;
}
void mergeWith (const Class& other)
{
jassert (*this == other);
if (other.classDeclaration.isValid())
classDeclaration = other.classDeclaration;
for (auto& m : other.members)
{
if (auto* existing = findMember (m.getName()))
existing->mergeWith (m);
else
members.add (m);
}
}
void nudgeAllCodeRanges (const String& file, int index, int delta)
{
for (MemberInfo& m : members) m.nudgeAllCodeRanges (file, index, delta);
for (MethodInfo& m : methods) m.nudgeAllCodeRanges (file, index, delta);
classDeclaration.nudge (file, index, delta);
}
void fileContentChanged (const String& file)
{
for (MemberInfo& m : members) m.fileContentChanged (file);
for (MethodInfo& m : methods) m.fileContentChanged (file);
classDeclaration.fileContentChanged (file);
}
Class (const ValueTree& v)
{
className = v[Ids::name];
instantiationFlags.isAbstract = v[Ids::abstract];
instantiationFlags.inAnonymousNamespace = v[Ids::anonymous];
instantiationFlags.noDefaultConstructor = v[Ids::noDefConstructor];
classDeclaration = v [Ids::classDecl].toString();
for (int i = 0; i < v.getNumChildren(); ++i)
members.add (MemberInfo (v.getChild(i)));
}
ValueTree toValueTree() const
{
ValueTree v (Ids::CLASS);
v.setProperty (Ids::name, className, nullptr);
v.setProperty (Ids::abstract, instantiationFlags.isAbstract, nullptr);
v.setProperty (Ids::anonymous, instantiationFlags.inAnonymousNamespace, nullptr);
v.setProperty (Ids::noDefConstructor, instantiationFlags.noDefaultConstructor, nullptr);
classDeclaration.writeToValueTree (v, Ids::classDecl);
for (const MemberInfo& m : members)
v.appendChild (m.toValueTree(), nullptr);
return v;
}
bool operator== (const Class& other) const noexcept { return className == other.className; }
bool operator!= (const Class& other) const noexcept { return ! operator== (other); }
bool operator< (const Class& other) const noexcept { return className < other.className; }
const Array<MemberInfo>& getMembers() const { return members; }
private:
String className;
Array<MemberInfo> members;
Array<MethodInfo> methods;
SourceCodeRange classDeclaration;
InstantiationFlags instantiationFlags;
JUCE_LEAK_DETECTOR (Class)
JUCE_DECLARE_WEAK_REFERENCEABLE (Class)
};
//==============================================================================
struct Namespace
{
Namespace() : name ("Global Namespace") {}
Namespace (const String& n, const String& full) : name (n), fullName (full) {}
bool isEmpty() const noexcept
{
for (const auto& n : namespaces)
if (! n.isEmpty())
return false;
return components.size() == 0;
}
int getTotalClassesAndNamespaces() const
{
int total = components.size();
for (const auto& n : namespaces)
total += n.getTotalClassesAndNamespaces();
return total;
}
void add (const Class& c, const String::CharPointerType& localName)
{
auto nextDoubleColon = CharacterFunctions::find (localName, CharPointer_ASCII ("::"));
if (nextDoubleColon.isEmpty())
merge (c);
else
getOrCreateNamespace (String (localName, nextDoubleColon))->add (c, nextDoubleColon + 2);
}
bool containsRecursively (const Class& c) const
{
if (components.contains (c))
return true;
for (const auto& n : namespaces)
if (n.containsRecursively (c))
return true;
return false;
}
const Class* findClass (const String& className) const
{
for (auto& c : components)
if (c.getName() == className)
return &c;
for (auto& n : namespaces)
if (auto* c = n.findClass (className))
return c;
return nullptr;
}
const MemberInfo* findClassMemberInfo (const String& className, const String& memberName) const
{
if (auto* classInfo = findClass (className))
return classInfo->findMember (memberName);
return nullptr;
}
void findClassesDeclaredInFile (Array<WeakReference<Class>>& results, const File& file)
{
for (int i = 0; i < components.size(); ++i)
{
auto& c = components.getReference (i);
if (c.isDeclaredInFile (file))
results.add (&c);
}
for (int i = 0; i < namespaces.size(); ++i)
namespaces.getReference (i).findClassesDeclaredInFile (results, file);
}
void merge (const Namespace& other)
{
if (components.size() == 0)
{
components = other.components;
}
else
{
for (const auto& c : other.components)
merge (c);
}
for (const auto& n : other.namespaces)
getOrCreateNamespace (n.name)->merge (n);
}
void merge (const Class& c)
{
const int existing = components.indexOf (c);
if (existing < 0)
components.add (c);
else
components.getReference (existing).mergeWith (c);
}
Namespace* findNamespace (const String& targetName)
{
for (int i = 0; i < namespaces.size(); ++i)
{
auto& n = namespaces.getReference (i);
if (n.name == targetName)
return &n;
}
return nullptr;
}
Namespace* createNamespace (const String& newName)
{
namespaces.add (Namespace (newName, fullName + "::" + newName));
return findNamespace (newName);
}
Namespace* getOrCreateNamespace (const String& newName)
{
if (auto* existing = findNamespace (newName))
return existing;
return createNamespace (newName);
}
void addInstantiableClasses (SortedSet<Class>& classes) const
{
for (const auto& c : components)
if (c.getInstantiationFlags().canBeInstantiated())
classes.add (c);
for (const auto& n : namespaces)
n.addInstantiableClasses (classes);
}
void swapWith (Namespace& other) noexcept
{
name.swapWith (other.name);
components.swapWith (other.components);
namespaces.swapWith (other.namespaces);
}
void nudgeAllCodeRanges (const String& file, int index, int delta)
{
for (int i = 0; i < components.size(); ++i)
components.getReference (i).nudgeAllCodeRanges (file, index, delta);
for (int i = 0; i < namespaces.size(); ++i)
namespaces.getReference (i).nudgeAllCodeRanges (file, index, delta);
}
void fileContentChanged (const String& file)
{
for (int i = 0; i < components.size(); ++i)
components.getReference (i).fileContentChanged (file);
for (int i = 0; i < namespaces.size(); ++i)
namespaces.getReference (i).fileContentChanged (file);
}
bool matches (const Namespace& other) const
{
if (name == other.name
&& components == other.components
&& namespaces.size() == other.namespaces.size())
{
for (int i = namespaces.size(); --i >= 0;)
if (! namespaces.getReference (i).matches (other.namespaces.getReference (i)))
return false;
return true;
}
return false;
}
void getAllClassNames (StringArray& results, const InstantiationFlags& disallowedFlags) const
{
for (const auto& c : components)
if (c.getInstantiationFlags().isDisallowed (disallowedFlags))
results.add (c.getName());
for (const auto& n : namespaces)
n.getAllClassNames (results, disallowedFlags);
}
ValueTree toValueTree() const
{
ValueTree v (Ids::CLASSLIST);
v.setProperty (Ids::name, name, nullptr);
for (const auto& c : components) v.appendChild (c.toValueTree(), nullptr);
for (const auto& n : namespaces) v.appendChild (n.toValueTree(), nullptr);
return v;
}
void loadFromValueTree (const ValueTree& v)
{
name = v[Ids::name];
for (int i = 0; i < v.getNumChildren(); ++i)
{
const ValueTree c (v.getChild(i));
if (c.hasType (Ids::CLASS))
components.add (Class (c));
else if (c.hasType (Ids::CLASSLIST))
createNamespace (c[Ids::name])->loadFromValueTree (c);
}
}
bool operator== (const Namespace& other) const noexcept { return name == other.name; }
bool operator!= (const Namespace& other) const noexcept { return ! operator== (other); }
bool operator< (const Namespace& other) const noexcept { return name < other.name; }
String name, fullName;
SortedSet<Class> components;
SortedSet<Namespace> namespaces;
JUCE_LEAK_DETECTOR (Namespace)
};
struct ClassList
{
ClassList() {}
void clear()
{
Namespace newNamespace;
globalNamespace.swapWith (newNamespace);
}
void registerComp (const Class& comp)
{
globalNamespace.add (comp, comp.getName().getCharPointer());
}
void merge (const ClassList& other)
{
globalNamespace.merge (other.globalNamespace);
}
void swapWith (ClassList& other) noexcept
{
globalNamespace.swapWith (other.globalNamespace);
}
//==============================================================================
ValueTree toValueTree() const
{
return globalNamespace.toValueTree();
}
static ClassList fromValueTree (const ValueTree& v)
{
ClassList l;
l.globalNamespace.loadFromValueTree (v);
return l;
}
Namespace globalNamespace;
bool operator== (const ClassList& other) const noexcept { return globalNamespace.matches (other.globalNamespace); }
bool operator!= (const ClassList& other) const noexcept { return ! operator== (other); }
private:
JUCE_LEAK_DETECTOR (ClassList)
};
};

View file

@ -1,295 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
//==============================================================================
struct MessageHandler
{
virtual ~MessageHandler() {}
virtual bool sendMessage (const ValueTree&) = 0;
static MemoryBlock convertMessage (const ValueTree& tree)
{
MemoryOutputStream out;
tree.writeToStream (out);
return out.getMemoryBlock();
}
static ValueTree convertMessage (const MemoryBlock& rawData)
{
return ValueTree::readFromData (rawData.getData(), rawData.getSize());
}
};
//==============================================================================
static inline Rectangle<int> varToRect (const var& v)
{
if (const Array<var>* obj = v.getArray())
{
if (obj->size() == 4)
{
int intArray[4];
for (int i = 0; i < 4; ++i)
{
const var& p = obj->getReference (i);
if (p.isInt() || p.isDouble() || p.isInt64())
intArray[i] = static_cast<int> (p);
else
return Rectangle<int>();
}
return Rectangle<int> (intArray[0], intArray[1], intArray[2], intArray[3]);
}
}
return Rectangle<int>();
}
static inline var rectToVar (const Rectangle<int>& rect)
{
Array<var> retval;
retval.add (var (rect.getX()));
retval.add (var (rect.getY()));
retval.add (var (rect.getWidth()));
retval.add (var (rect.getHeight()));
return var (retval);
}
//==============================================================================
namespace MessageTypes
{
inline bool send (MessageHandler& target, const ValueTree& v)
{
bool result = target.sendMessage (v);
if (! result)
Logger::outputDebugString ("*** Message failed: " + v.getType().toString());
return result;
}
inline bool sendPing (MessageHandler& target)
{
return send (target, ValueTree (PING));
}
//==============================================================================
// client -> server
inline void sendOpenPreview (MessageHandler& target, const ClassDatabase::Class& comp, Rectangle<int> mainWindowRect)
{
ValueTree v (OPEN_PREVIEW);
v.setProperty (Ids::name, comp.getName(), nullptr);
v.setProperty (Ids::bounds, rectToVar (mainWindowRect), nullptr);
send (target, v);
}
inline void sendReinstantiate (MessageHandler& target)
{
send (target, ValueTree (RELOAD));
}
inline void sendFileChanges (MessageHandler& target, const Array<CodeChange>& changes, const File& file)
{
ValueTree changesMessage (MessageTypes::LIVE_FILE_CHANGES);
changesMessage.setProperty (Ids::file, file.getFullPathName(), nullptr);
for (const CodeChange& change : changes)
{
ValueTree v (CHANGE);
v.setProperty (Ids::start, change.range.getStart(), nullptr);
v.setProperty (Ids::end, change.range.getEnd(), nullptr);
v.setProperty (Ids::text, change.text, nullptr);
changesMessage.appendChild (v, nullptr);
}
send (target, changesMessage);
}
inline Array<CodeChange> getChangeArray (const ValueTree& changes)
{
Array<CodeChange> result;
for (int i = 0; i < changes.getNumChildren(); ++i)
{
const ValueTree& v = changes.getChild (i);
result.add (CodeChange (Range<int> (v[Ids::start], v[Ids::end]), v[Ids::text]));
}
return result;
}
inline void sendFileContentFullUpdate (MessageHandler& target, const File& file, const String& text)
{
ValueTree v (LIVE_FILE_UPDATE);
v.setProperty (Ids::file, file.getFullPathName(), nullptr);
v.setProperty (Ids::text, text, nullptr);
send (target, v);
}
inline void sendHandleFileReset (MessageHandler& target, const File& file)
{
ValueTree v (LIVE_FILE_RESET);
v.setProperty (Ids::file, file.getFullPathName(), nullptr);
send (target, v);
}
inline void sendNewBuild (MessageHandler& target, const ProjectBuildInfo& build)
{
send (target, build.tree);
}
inline void sendCleanAll (MessageHandler& target)
{
send (target, ValueTree (CLEAN_ALL));
}
inline void sendNewDiagnosticList (MessageHandler& target, const ValueTree& list)
{
send (target, list);
}
inline void sendEmptyDiagnosticList (MessageHandler& target)
{
send (target, ValueTree (MessageTypes::DIAGNOSTIC_LIST));
}
inline void sendProcessActivationState (MessageHandler& target, bool isNowActive)
{
ValueTree v (FOREGROUND);
v.setProperty (Ids::parentActive, isNowActive, nullptr);
send (target, v);
}
inline void sendLaunchApp (MessageHandler& target) { send (target, ValueTree (LAUNCH_APP)); }
inline void sendQuit (MessageHandler& target) { send (target, ValueTree (QUIT_SERVER)); }
inline void sendShouldCloseIDE (MessageHandler& target) { send (target, ValueTree (QUIT_IDE)); }
//==============================================================================
// server -> client
inline void sendNewClassList (MessageHandler& target, const ClassDatabase::ClassList& classes)
{
send (target, classes.toValueTree());
}
inline void sendCrash (MessageHandler& target, const String& message)
{
ValueTree v (CRASH);
v.setProperty (Ids::message, message, nullptr);
send (target, v);
}
inline void sendSystemHeadersMissing (MessageHandler& target)
{
send (target, ValueTree (MISSING_SYSTEM_HEADERS));
}
inline void sendBuildFailed (MessageHandler& target)
{
send (target, ValueTree (BUILD_FAILED));
}
inline void sendNewActivityList (MessageHandler& target, const StringArray& list)
{
ValueTree v (ACTIVITY_LIST);
v.setProperty (Ids::list, concatenateListOfStrings (list), nullptr);
send (target, v);
}
inline void sendChangeCode (MessageHandler& target, const String& location, const String& newText)
{
if (location.isNotEmpty())
{
ValueTree v (CHANGE_CODE);
v.setProperty (Ids::position, location, nullptr);
v.setProperty (Ids::text, newText, nullptr);
send (target, v);
}
}
inline void sendHighlightCode (MessageHandler& target, const String& location)
{
if (location.isNotEmpty())
{
ValueTree v (HIGHLIGHT_CODE);
v.setProperty (Ids::position, location, nullptr);
send (target, v);
}
}
inline void sendAppLaunched (MessageHandler& target) { send (target, ValueTree (LAUNCHED)); }
inline void sendAppQuit (MessageHandler& target) { send (target, ValueTree (APPQUIT)); }
inline void sendKeyPress (MessageHandler& target, const String& className, const String& keyDesc)
{
ValueTree v (KEY);
v.setProperty (Ids::class_, className, nullptr);
v.setProperty (Ids::key, keyDesc, nullptr);
send (target, v);
}
//==============================================================================
template <class MessageHandlerType>
static void dispatchToClient (MessageHandlerType& target, const ValueTree& v)
{
if (v.hasType (DIAGNOSTIC_LIST)) target.handleNewDiagnosticList (v);
else if (v.hasType (ACTIVITY_LIST)) target.handleActivityListChanged (separateJoinedStrings (v [Ids::list]));
else if (v.hasType (Ids::CLASSLIST)) target.handleClassListChanged (v);
else if (v.hasType (BUILD_FAILED)) target.handleBuildFailed();
else if (v.hasType (CHANGE_CODE)) target.handleChangeCode (v [Ids::position].toString(), v [Ids::text]);
else if (v.hasType (HIGHLIGHT_CODE)) target.handleHighlightCode (v [Ids::position].toString());
else if (v.hasType (LAUNCHED)) target.handleAppLaunched();
else if (v.hasType (APPQUIT)) target.handleAppQuit();
else if (v.hasType (PING)) target.handlePing();
else if (v.hasType (CRASH)) target.handleCrash (v [Ids::message]);
else if (v.hasType (KEY)) target.handleKeyPress (v[Ids::class_], KeyPress::createFromDescription (v[Ids::key]));
else if (v.hasType (QUIT_IDE)) target.handleCloseIDE();
else if (v.hasType (MISSING_SYSTEM_HEADERS)) target.handleMissingSystemHeaders();
else jassertfalse;
}
template <class MessageHandlerType>
static void dispatchToServer (MessageHandlerType& target, const ValueTree& v)
{
if (v.hasType (CLEAN_ALL)) target.handleCleanAll();
else if (v.hasType (BUILDINFO)) target.handleNewBuildSettings (ProjectBuildInfo (v));
else if (v.hasType (OPEN_PREVIEW)) target.handleOpenPreview (v[Ids::name], varToRect (v[Ids::bounds]));
else if (v.hasType (RELOAD)) target.handleReinstantiatePreviews();
else if (v.hasType (LAUNCH_APP)) target.handleLaunchApp();
else if (v.hasType (LIVE_FILE_CHANGES)) target.handleLiveFileChanges (v[Ids::file].toString(), getChangeArray (v));
else if (v.hasType (LIVE_FILE_UPDATE)) target.handleLiveFileFullUpdate (v[Ids::file].toString(), v[Ids::text]);
else if (v.hasType (LIVE_FILE_RESET)) target.handleResetLiveFileContent (v[Ids::file].toString());
else if (v.hasType (FOREGROUND)) target.handleProcessActivationState (v[Ids::parentActive]);
else if (v.hasType (PING)) target.handlePing();
else jassertfalse;
}
}

View file

@ -1,907 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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 "../Application/jucer_Application.h"
#include "../ProjectSaving/jucer_ProjectExporter.h"
#include "jucer_MessageIDs.h"
#include "jucer_CppHelpers.h"
#include "jucer_SourceCodeRange.h"
#include "jucer_ClassDatabase.h"
#include "jucer_DiagnosticMessage.h"
#include "jucer_ProjectBuildInfo.h"
#include "jucer_ClientServerMessages.h"
#include "jucer_CompileEngineClient.h"
#include "jucer_CompileEngineServer.h"
#include "jucer_CompileEngineSettings.h"
#include "../Project/UI/jucer_ProjectContentComponent.h"
#ifndef RUN_CLANG_IN_CHILD_PROCESS
#error
#endif
//==============================================================================
static File getProjucerTempFolder() noexcept
{
#if JUCE_MAC
return { "~/Library/Caches/com.juce.projucer" };
#else
return File::getSpecialLocation (File::tempDirectory).getChildFile ("com.juce.projucer");
#endif
}
static File getCacheLocationForProject (Project& project) noexcept
{
auto cacheFolderName = project.getProjectFilenameRootString() + "_" + project.getProjectUIDString();
#if JUCE_DEBUG
cacheFolderName += "_debug";
#endif
return getProjucerTempFolder().getChildFile ("Intermediate Files").getChildFile (cacheFolderName);
}
//==============================================================================
class ClientIPC : public MessageHandler,
private InterprocessConnection,
private Timer
{
public:
explicit ClientIPC (CompileEngineChildProcess& cp)
: InterprocessConnection (true), owner (cp)
{
launchServer();
}
~ClientIPC()
{
#if RUN_CLANG_IN_CHILD_PROCESS
if (childProcess.isRunning())
{
#if JUCE_DEBUG
killServerPolitely();
#else
// in release builds we don't want to wait
// for the server to clean up and shut down
killServerWithoutMercy();
#endif
}
#endif
}
void launchServer()
{
DBG ("Client: Launching Server...");
auto pipeName = "ipc_" + String::toHexString (Random().nextInt64());
auto command = createCommandLineForLaunchingServer (pipeName, owner.project.getProjectUIDString(),
getCacheLocationForProject (owner.project));
#if RUN_CLANG_IN_CHILD_PROCESS
if (! childProcess.start (command))
jassertfalse;
#else
server = createClangServer (command);
#endif
for (int i = 0; i < 20; ++i)
{
if (connectToPipe (pipeName, 10000))
{
MessageTypes::sendPing (*this);
break;
}
Thread::sleep (50);
}
jassert (isConnected());
startTimer (serverKeepAliveTimeout);
}
void killServerPolitely()
{
DBG ("Client: Killing Server...");
MessageTypes::sendQuit (*this);
disconnect();
stopTimer();
#if RUN_CLANG_IN_CHILD_PROCESS
childProcess.waitForProcessToFinish (5000);
#endif
killServerWithoutMercy();
}
void killServerWithoutMercy()
{
disconnect();
stopTimer();
#if RUN_CLANG_IN_CHILD_PROCESS
childProcess.kill();
#else
destroyClangServer (server);
server = nullptr;
#endif
}
void connectionMade()
{
DBG ("Client: connected");
stopTimer();
}
void connectionLost()
{
DBG ("Client: disconnected");
startTimer (100);
}
bool sendMessage (const ValueTree& m)
{
return InterprocessConnection::sendMessage (MessageHandler::convertMessage (m));
}
void messageReceived (const MemoryBlock& message)
{
#if RUN_CLANG_IN_CHILD_PROCESS
startTimer (serverKeepAliveTimeout);
#else
stopTimer();
#endif
MessageTypes::dispatchToClient (owner, MessageHandler::convertMessage (message));
}
enum { serverKeepAliveTimeout = 10000 };
private:
CompileEngineChildProcess& owner;
#if RUN_CLANG_IN_CHILD_PROCESS
ChildProcess childProcess;
#else
void* server;
#endif
void timerCallback()
{
stopTimer();
owner.handleCrash (String());
}
};
//==============================================================================
class CompileEngineChildProcess::ChildProcess : private ValueTree::Listener,
private Timer
{
public:
ChildProcess (CompileEngineChildProcess& proc, Project& p)
: owner (proc), project (p)
{
projectRoot = project.getProjectRoot();
restartServer();
projectRoot.addListener (this);
openedOk = true;
}
~ChildProcess() override
{
projectRoot.removeListener (this);
if (isRunningApp && server != nullptr)
server->killServerWithoutMercy();
}
void restartServer()
{
server.reset (new ClientIPC (owner));
sendRebuild();
}
void sendRebuild()
{
stopTimer();
ProjectBuildInfo build;
if (! doesProjectMatchSavedHeaderState (project))
{
MessageTypes::sendNewBuild (*server, build);
owner.errorList.resetToError ("Project structure does not match the saved headers! "
"Please re-save your project to enable compilation");
return;
}
if (areAnyModulesMissing (project))
{
MessageTypes::sendNewBuild (*server, build);
owner.errorList.resetToError ("Some of your JUCE modules can't be found! "
"Please check that all the module paths are correct");
return;
}
build.setSystemIncludes (getSystemIncludePaths());
build.setUserIncludes (getUserIncludes());
build.setGlobalDefs (getGlobalDefs());
build.setCompileFlags (project.getCompileEngineSettings().getExtraCompilerFlagsString());
build.setExtraDLLs (getExtraDLLs());
build.setJuceModulesFolder (project.getEnabledModules().getDefaultModulesFolder().getFullPathName());
build.setUtilsCppInclude (project.getAppIncludeFile().getFullPathName());
build.setWindowsTargetPlatformVersion (project.getCompileEngineSettings().getWindowsTargetPlatformVersionString());
scanForProjectFiles (project, build);
owner.updateAllEditors();
MessageTypes::sendNewBuild (*server, build);
}
void cleanAll()
{
MessageTypes::sendCleanAll (*server);
sendRebuild();
}
void reinstantiatePreviews()
{
MessageTypes::sendReinstantiate (*server);
}
bool launchApp()
{
MessageTypes::sendLaunchApp (*server);
return true;
}
std::unique_ptr<ClientIPC> server;
bool openedOk = false;
bool isRunningApp = false;
private:
CompileEngineChildProcess& owner;
Project& project;
ValueTree projectRoot;
void projectStructureChanged()
{
startTimer (100);
}
void timerCallback() override
{
sendRebuild();
}
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { projectStructureChanged(); }
void valueTreeChildAdded (ValueTree&, ValueTree&) override { projectStructureChanged(); }
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { projectStructureChanged(); }
void valueTreeParentChanged (ValueTree&) override { projectStructureChanged(); }
String getGlobalDefs()
{
auto mergeDefs = [] (const StringPairArray& inDefs) -> String
{
StringArray outDefs;
for (int i = 0; i < inDefs.size(); ++i)
{
auto def = inDefs.getAllKeys()[i];
auto value = inDefs.getAllValues()[i];
if (value.isNotEmpty())
def << "=" << value;
outDefs.add (def);
}
return outDefs.joinIntoString (" ");
};
StringArray defs;
if (! project.shouldUseAppConfig())
defs.add (mergeDefs (project.getAppConfigDefs()));
defs.add (project.getCompileEngineSettings().getExtraPreprocessorDefsString());
defs.add (mergeDefs (project.getPreprocessorDefs()));
for (Project::ExporterIterator exporter (project); exporter.next();)
if (exporter->canLaunchProject())
defs.add (exporter->getExporterIdentifierMacro() + "=1");
defs.removeEmptyStrings();
return defs.joinIntoString (" ");
}
static void scanProjectItem (const Project::Item& projectItem, Array<File>& compileUnits, Array<File>& userFiles)
{
if (projectItem.isGroup())
{
for (int i = 0; i < projectItem.getNumChildren(); ++i)
scanProjectItem (projectItem.getChild(i), compileUnits, userFiles);
return;
}
if (projectItem.shouldBeCompiled())
{
auto f = projectItem.getFile();
if (f.exists())
compileUnits.add (f);
}
if (projectItem.shouldBeAddedToTargetProject() && ! projectItem.shouldBeAddedToBinaryResources())
{
auto f = projectItem.getFile();
if (f.exists())
userFiles.add (f);
}
}
void scanForProjectFiles (Project& proj, ProjectBuildInfo& build)
{
Array<File> compileUnits, userFiles;
scanProjectItem (proj.getMainGroup(), compileUnits, userFiles);
{
auto isVSTHost = project.getEnabledModules().isModuleEnabled ("juce_audio_processors")
&& (project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST3", false) || project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST", false));
auto isPluginProject = proj.isAudioPluginProject();
OwnedArray<LibraryModule> modules;
proj.getEnabledModules().createRequiredModules (modules);
for (Project::ExporterIterator exporter (proj); exporter.next();)
{
if (exporter->canLaunchProject())
{
for (auto* m : modules)
{
auto copyLocally = proj.getEnabledModules().shouldCopyModuleFilesLocally (m->moduleInfo.getID());
auto localModuleFolder = copyLocally ? proj.getLocalModuleFolder (m->moduleInfo.getID())
: m->moduleInfo.getFolder();
m->findAndAddCompiledUnits (*exporter, nullptr, compileUnits,
isPluginProject || isVSTHost ? build_tools::ProjectType::Target::SharedCodeTarget
: build_tools::ProjectType::Target::unspecified);
if (isPluginProject || isVSTHost)
m->findAndAddCompiledUnits (*exporter, nullptr, compileUnits,
build_tools::ProjectType::Target::StandalonePlugIn);
}
break;
}
}
}
for (int i = 0; ; ++i)
{
auto binaryDataCpp = proj.getBinaryDataCppFile (i);
if (! binaryDataCpp.exists())
break;
compileUnits.add (binaryDataCpp);
}
for (auto i = compileUnits.size(); --i >= 0;)
if (compileUnits.getReference(i).hasFileExtension (".r"))
compileUnits.remove (i);
build.setFiles (compileUnits, userFiles);
}
static bool doesProjectMatchSavedHeaderState (Project& project)
{
auto liveModules = project.getProjectRoot().getChildWithName (Ids::MODULES);
if (auto xml = parseXMLIfTagMatches (project.getFile(), Ids::JUCERPROJECT.toString()))
{
auto diskModules = ValueTree::fromXml (*xml).getChildWithName (Ids::MODULES);
return liveModules.isEquivalentTo (diskModules);
}
return false;
}
static bool areAnyModulesMissing (Project& project)
{
OwnedArray<LibraryModule> modules;
project.getEnabledModules().createRequiredModules (modules);
for (auto* module : modules)
if (! module->getFolder().isDirectory())
return true;
return false;
}
StringArray getUserIncludes()
{
StringArray paths;
paths.add (project.getGeneratedCodeFolder().getFullPathName());
paths.addArray (getSearchPathsFromString (project.getCompileEngineSettings().getUserHeaderPathString()));
return convertSearchPathsToAbsolute (paths);
}
StringArray getSystemIncludePaths()
{
StringArray paths;
paths.add (project.getGeneratedCodeFolder().getFullPathName());
paths.addArray (getSearchPathsFromString (project.getCompileEngineSettings().getSystemHeaderPathString()));
auto isVSTHost = project.getEnabledModules().isModuleEnabled ("juce_audio_processors")
&& (project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST3", false)
|| project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST", false));
OwnedArray<LibraryModule> modules;
project.getEnabledModules().createRequiredModules (modules);
for (auto* module : modules)
{
paths.addIfNotAlreadyThere (module->getFolder().getParentDirectory().getFullPathName());
if (module->getID() == "juce_audio_processors" && ((project.isAudioPluginProject() || isVSTHost)
&& ! project.isConfigFlagEnabled ("JUCE_CUSTOM_VST3_SDK")))
{
paths.addIfNotAlreadyThere (module->getFolder().getChildFile ("format_types").getChildFile ("VST3_SDK").getFullPathName());
}
}
return convertSearchPathsToAbsolute (paths);
}
StringArray convertSearchPathsToAbsolute (const StringArray& paths) const
{
StringArray s;
const File root (project.getProjectFolder());
for (String p : paths)
s.add (root.getChildFile (p).getFullPathName());
return s;
}
StringArray getExtraDLLs()
{
auto dlls = StringArray::fromTokens (project.getCompileEngineSettings().getExtraDLLsString(), "\n\r,", {});
dlls.trim();
dlls.removeEmptyStrings();
return dlls;
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcess)
};
//==============================================================================
CompileEngineChildProcess::CompileEngineChildProcess (Project& p)
: project (p)
{
ProjucerApplication::getApp().openDocumentManager.addListener (this);
createProcess();
errorList.setWarningsEnabled (project.getCompileEngineSettings().areWarningsEnabled());
}
CompileEngineChildProcess::~CompileEngineChildProcess()
{
ProjucerApplication::getApp().openDocumentManager.removeListener (this);
}
void CompileEngineChildProcess::createProcess()
{
jassert (process == nullptr);
process.reset (new ChildProcess (*this, project));
if (! process->openedOk)
process.reset();
updateAllEditors();
}
void CompileEngineChildProcess::cleanAll()
{
if (process != nullptr)
process->cleanAll();
}
void CompileEngineChildProcess::openPreview (const ClassDatabase::Class& comp)
{
if (process != nullptr)
{
MainWindow* projectWindow = nullptr;
OwnedArray<MainWindow>& windows = ProjucerApplication::getApp().mainWindowList.windows;
for (int i = 0; i < windows.size(); ++i)
{
if (MainWindow* w = windows[i])
{
if (w->getProject() == &project)
{
projectWindow = w;
break;
}
}
}
Rectangle<int> mainWindowRect;
if (projectWindow != nullptr)
mainWindowRect = projectWindow->getBounds();
MessageTypes::sendOpenPreview (*process->server, comp, mainWindowRect);
}
}
void CompileEngineChildProcess::reinstantiatePreviews()
{
if (process != nullptr)
process->reinstantiatePreviews();
}
void CompileEngineChildProcess::processActivationChanged (bool isForeground)
{
if (process != nullptr)
MessageTypes::sendProcessActivationState (*process->server, isForeground);
}
//==============================================================================
bool CompileEngineChildProcess::canLaunchApp() const
{
return process != nullptr
&& runningAppProcess == nullptr
&& activityList.getNumActivities() == 0
&& errorList.getNumErrors() == 0
&& project.getProjectType().isGUIApplication();
}
void CompileEngineChildProcess::launchApp()
{
if (process != nullptr)
process->launchApp();
}
bool CompileEngineChildProcess::canKillApp() const
{
return runningAppProcess != nullptr;
}
void CompileEngineChildProcess::killApp()
{
runningAppProcess.reset();
}
void CompileEngineChildProcess::handleAppLaunched()
{
runningAppProcess.reset (process.release());
runningAppProcess->isRunningApp = true;
createProcess();
}
void CompileEngineChildProcess::handleAppQuit()
{
DBG ("handleAppQuit");
runningAppProcess.reset();
}
bool CompileEngineChildProcess::isAppRunning() const noexcept
{
return runningAppProcess != nullptr && runningAppProcess->isRunningApp;
}
//==============================================================================
struct CompileEngineChildProcess::Editor : private CodeDocument::Listener,
private Timer
{
Editor (CompileEngineChildProcess& ccp, const File& f, CodeDocument& doc)
: owner (ccp), file (f), document (doc), transactionTimer (doc)
{
sendFullUpdate();
document.addListener (this);
}
~Editor() override
{
document.removeListener (this);
}
void codeDocumentTextInserted (const String& newText, int insertIndex) override
{
CodeChange (Range<int> (insertIndex, insertIndex), newText).addToList (pendingChanges);
startEditorChangeTimer();
transactionTimer.stopTimer();
owner.lastComponentList.globalNamespace
.nudgeAllCodeRanges (file.getFullPathName(), insertIndex, newText.length());
}
void codeDocumentTextDeleted (int start, int end) override
{
CodeChange (Range<int> (start, end), String()).addToList (pendingChanges);
startEditorChangeTimer();
transactionTimer.stopTimer();
owner.lastComponentList.globalNamespace
.nudgeAllCodeRanges (file.getFullPathName(), start, start - end);
}
void sendFullUpdate()
{
reset();
if (owner.process != nullptr)
MessageTypes::sendFileContentFullUpdate (*owner.process->server, file, document.getAllContent());
}
bool flushEditorChanges()
{
if (pendingChanges.size() > 0)
{
if (owner.process != nullptr && owner.process->server != nullptr)
MessageTypes::sendFileChanges (*owner.process->server, pendingChanges, file);
reset();
return true;
}
stopTimer();
return false;
}
void reset()
{
stopTimer();
pendingChanges.clear();
}
void startTransactionTimer()
{
transactionTimer.startTimer (1000);
}
void startEditorChangeTimer()
{
startTimer (200);
}
CompileEngineChildProcess& owner;
File file;
CodeDocument& document;
private:
Array<CodeChange> pendingChanges;
void timerCallback() override
{
if (owner.project.getCompileEngineSettings().isContinuousRebuildEnabled())
flushEditorChanges();
else
stopTimer();
}
struct TransactionTimer : public Timer
{
explicit TransactionTimer (CodeDocument& doc) : document (doc) {}
void timerCallback() override
{
stopTimer();
document.newTransaction();
}
CodeDocument& document;
};
TransactionTimer transactionTimer;
};
void CompileEngineChildProcess::editorOpened (const File& file, CodeDocument& document)
{
editors.add (new Editor (*this, file, document));
}
bool CompileEngineChildProcess::documentAboutToClose (OpenDocumentManager::Document* document)
{
for (int i = editors.size(); --i >= 0;)
{
if (document->getFile() == editors.getUnchecked(i)->file)
{
const File f (editors.getUnchecked(i)->file);
editors.remove (i);
if (process != nullptr)
MessageTypes::sendHandleFileReset (*process->server, f);
}
}
return true;
}
void CompileEngineChildProcess::updateAllEditors()
{
for (int i = editors.size(); --i >= 0;)
editors.getUnchecked(i)->sendFullUpdate();
}
//==============================================================================
void CompileEngineChildProcess::handleCrash (const String& message)
{
Logger::writeToLog ("*** Child process crashed: " + message);
if (crashHandler != nullptr)
crashHandler (message);
}
void CompileEngineChildProcess::handleNewDiagnosticList (const ValueTree& l) { errorList.setList (l); }
void CompileEngineChildProcess::handleActivityListChanged (const StringArray& l) { activityList.setList (l); }
void CompileEngineChildProcess::handleCloseIDE()
{
if (JUCEApplication* app = JUCEApplication::getInstance())
app->systemRequestedQuit();
}
void CompileEngineChildProcess::handleMissingSystemHeaders()
{
if (ProjectContentComponent* p = findProjectContentComponent())
p->handleMissingSystemHeaders();
}
void CompileEngineChildProcess::handleKeyPress (const String& className, const KeyPress& key)
{
ApplicationCommandManager& commandManager = ProjucerApplication::getCommandManager();
CommandID command = commandManager.getKeyMappings()->findCommandForKeyPress (key);
if (command == StandardApplicationCommandIDs::undo)
{
handleUndoInEditor (className);
}
else if (command == StandardApplicationCommandIDs::redo)
{
handleRedoInEditor (className);
}
else if (ApplicationCommandTarget* const target = ApplicationCommandManager::findTargetForComponent (findProjectContentComponent()))
{
commandManager.setFirstCommandTarget (target);
commandManager.getKeyMappings()->keyPressed (key, findProjectContentComponent());
commandManager.setFirstCommandTarget (nullptr);
}
}
void CompileEngineChildProcess::handleUndoInEditor (const String& /*className*/)
{
}
void CompileEngineChildProcess::handleRedoInEditor (const String& /*className*/)
{
}
void CompileEngineChildProcess::handleClassListChanged (const ValueTree& newList)
{
lastComponentList = ClassDatabase::ClassList::fromValueTree (newList);
activityList.sendClassListChangedMessage (lastComponentList);
}
void CompileEngineChildProcess::handleBuildFailed()
{
ProjucerApplication::getCommandManager().commandStatusChanged();
}
void CompileEngineChildProcess::handleChangeCode (const SourceCodeRange& location, const String& newText)
{
if (Editor* ed = getOrOpenEditorFor (location.file))
{
if (ed->flushEditorChanges())
return; // client-side editor changes were pending, so deal with them first, and discard
// the incoming change, whose position may now be wrong.
ed->document.deleteSection (location.range.getStart(), location.range.getEnd());
ed->document.insertText (location.range.getStart(), newText);
// deliberately clear the messages that we just added, to avoid these changes being
// sent to the server (which will already have processed the same ones locally)
ed->reset();
ed->startTransactionTimer();
}
}
void CompileEngineChildProcess::handlePing()
{
}
//==============================================================================
void CompileEngineChildProcess::flushEditorChanges()
{
for (Editor* ed : editors)
ed->flushEditorChanges();
}
ProjectContentComponent* CompileEngineChildProcess::findProjectContentComponent() const
{
for (MainWindow* mw : ProjucerApplication::getApp().mainWindowList.windows)
if (mw->getProject() == &project)
return mw->getProjectContentComponent();
return nullptr;
}
CompileEngineChildProcess::Editor* CompileEngineChildProcess::getOrOpenEditorFor (const File& file)
{
for (Editor* ed : editors)
if (ed->file == file)
return ed;
if (ProjectContentComponent* pcc = findProjectContentComponent())
if (pcc->showEditorForFile (file, false))
return getOrOpenEditorFor (file);
return nullptr;
}
void CompileEngineChildProcess::handleHighlightCode (const SourceCodeRange& location)
{
ProjectContentComponent* pcc = findProjectContentComponent();
if (pcc != nullptr && pcc->showEditorForFile (location.file, false))
{
SourceCodeEditor* sce = dynamic_cast <SourceCodeEditor*> (pcc->getEditorComponent());
if (sce != nullptr && sce->editor != nullptr)
{
sce->highlight (location.range, true);
Process::makeForegroundProcess();
CodeEditorComponent& ed = *sce->editor;
ed.getTopLevelComponent()->toFront (false);
ed.grabKeyboardFocus();
}
}
}
void CompileEngineChildProcess::cleanAllCachedFilesForProject (Project& p)
{
File cacheFolder (getCacheLocationForProject (p));
if (cacheFolder.isDirectory())
cacheFolder.deleteRecursively();
}

View file

@ -1,155 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
#include "jucer_ActivityList.h"
#include "jucer_ErrorList.h"
class Project;
//==============================================================================
class CompileEngineChildProcess : public ReferenceCountedObject,
private OpenDocumentManager::DocumentCloseListener
{
public:
CompileEngineChildProcess (Project&);
~CompileEngineChildProcess() override;
//==============================================================================
bool openedOk() const { return process != nullptr; }
void editorOpened (const File& file, CodeDocument& document);
bool documentAboutToClose (OpenDocumentManager::Document*) override;
//==============================================================================
void cleanAll();
void openPreview (const ClassDatabase::Class&);
void reinstantiatePreviews();
void processActivationChanged (bool isForeground);
//==============================================================================
bool canLaunchApp() const;
void launchApp();
bool canKillApp() const;
void killApp();
bool isAppRunning() const noexcept;
//==============================================================================
const ClassDatabase::ClassList& getComponentList() const { return lastComponentList; }
//==============================================================================
void flushEditorChanges();
static void cleanAllCachedFilesForProject (Project&);
//==============================================================================
Project& project;
ActivityList activityList;
ErrorList errorList;
//==============================================================================
std::function<void (const String&)> crashHandler;
//==============================================================================
// from server..
void handleNewDiagnosticList (const ValueTree& newList);
void handleClearErrors();
void handleActivityListChanged (const StringArray&);
void handleClassListChanged (const ValueTree& newList);
void handleBuildFailed();
void handleChangeCode (const SourceCodeRange& location, const String& newText);
void handleAppLaunched();
void handleAppQuit();
void handleHighlightCode (const SourceCodeRange& location);
void handlePing();
void handleCrash (const String& message);
void handleCloseIDE();
void handleKeyPress (const String& className, const KeyPress& key);
void handleUndoInEditor (const String& className);
void handleRedoInEditor (const String& className);
void handleMissingSystemHeaders();
using Ptr = ReferenceCountedObjectPtr<CompileEngineChildProcess>;
private:
//==============================================================================
class ChildProcess;
std::unique_ptr<ChildProcess> process, runningAppProcess;
ClassDatabase::ClassList lastComponentList;
struct Editor;
OwnedArray<Editor> editors;
void updateAllEditors();
void createProcess();
Editor* getOrOpenEditorFor (const File&);
ProjectContentComponent* findProjectContentComponent() const;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CompileEngineChildProcess)
};
//==============================================================================
struct ChildProcessCache
{
ChildProcessCache() {}
CompileEngineChildProcess::Ptr getExisting (Project& project) const noexcept
{
for (auto& p : processes)
if (&(p->project) == &project)
return *p;
return {};
}
CompileEngineChildProcess::Ptr getOrCreate (Project& project)
{
if (auto p = getExisting (project))
return p;
auto p = new CompileEngineChildProcess (project);
tellNewProcessAboutExistingEditors (*p);
processes.add (p);
return *p;
}
static void tellNewProcessAboutExistingEditors (CompileEngineChildProcess& process)
{
auto& odm = ProjucerApplication::getApp().openDocumentManager;
for (int i = odm.getNumOpenDocuments(); --i >= 0;)
if (auto d = dynamic_cast<SourceCodeDocument*> (odm.getOpenDocument (i)))
process.editorOpened (d->getFile(), d->getCodeDocument());
}
void removeOrphans()
{
processes.clear();
}
private:
ReferenceCountedArray<CompileEngineChildProcess> processes;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessCache)
};

View file

@ -1,201 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
#include "jucer_LiveCodeBuilderDLL.h"
//==============================================================================
struct CompileEngineDLL : private DeletedAtShutdown
{
CompileEngineDLL()
{
tryLoadDll();
}
~CompileEngineDLL()
{
shutdown();
clearSingletonInstance();
}
bool tryLoadDll()
{
// never load the dynamic lib multiple times
if (! isLoaded())
{
auto f = findDLLFile();
if (f != File() && dll.open (f.getLinkedTarget().getFullPathName()))
{
#define INIT_LIVE_DLL_FN(name, returnType, params) name = (name##_type) dll.getFunction (#name);
LIVE_DLL_FUNCTIONS (INIT_LIVE_DLL_FN);
#undef INIT_LIVE_DLL_FN
return true;
}
return false;
}
return true;
}
void initialise (CrashCallbackFunction crashFn, QuitCallbackFunction quitFn, bool setupSignals)
{
if (isLoaded())
projucer_initialise (crashFn, quitFn, setPropertyCallback, getPropertyCallback, setupSignals);
}
void shutdown()
{
if (isLoaded())
projucer_shutdown();
}
bool isLoaded() const
{
#define CHECK_LIVE_DLL_FN(name, returnType, params) if (name == nullptr) return false;
LIVE_DLL_FUNCTIONS (CHECK_LIVE_DLL_FN);
#undef CHECK_LIVE_DLL_FN
return projucer_getVersion() == requiredVersion;
}
#define DECLARE_LIVE_DLL_FN(name, returnType, params) \
typedef returnType (*name##_type) params; \
name##_type name = nullptr;
LIVE_DLL_FUNCTIONS (DECLARE_LIVE_DLL_FN)
#undef DECLARE_LIVE_DLL_FN
static String getDLLName()
{
#if JUCE_MAC
return "JUCECompileEngine.dylib";
#elif JUCE_LINUX || JUCE_BSD
return "JUCECompileEngine.so";
#elif JUCE_WINDOWS
return "JUCECompileEngine.dll";
#else
#error
return "JUCECompileEngine.so";
#endif
}
static File getVersionedUserAppSupportFolder()
{
auto userAppData = File::getSpecialLocation (File::userApplicationDataDirectory);
#if JUCE_MAC
userAppData = userAppData.getChildFile ("Application Support");
#endif
return userAppData.getChildFile ("Projucer").getChildFile (String ("CompileEngine-") + ProjectInfo::versionString);
}
JUCE_DECLARE_SINGLETON (CompileEngineDLL, false)
private:
DynamicLibrary dll;
enum { requiredVersion = 2 };
static File findDLLFile()
{
auto dllFile = File();
if (tryFindDLLFileInAppFolder (dllFile))
return dllFile;
#if JUCE_MAC
if (tryFindDLLFileInAppBundle(dllFile))
return dllFile;
#endif
if (tryFindDLLFileInAppConfigFolder (dllFile))
return dllFile;
return {};
}
#if JUCE_MAC
static bool tryFindDLLFileInAppBundle (File& outFile)
{
File currentAppFile (File::getSpecialLocation (File::currentApplicationFile));
return tryFindDLLFileInFolder (currentAppFile.getChildFile ("Contents"), outFile);
}
#endif
static bool tryFindDLLFileInAppFolder (File& outFile)
{
auto currentAppFile = File::getSpecialLocation (File::currentApplicationFile);
return tryFindDLLFileInFolder (currentAppFile.getParentDirectory(), outFile);
}
static bool tryFindDLLFileInAppConfigFolder (File& outFile)
{
auto userAppDataFolder = getVersionedUserAppSupportFolder();
return tryFindDLLFileInFolder (userAppDataFolder, outFile);
}
static bool tryFindDLLFileInFolder(File folder, File& outFile)
{
auto file = folder.getChildFile (getDLLName());
if (isDLLFile (file))
{
outFile = file;
return true;
}
return false;
}
static bool isDLLFile (const File& f)
{
return f.getFileName().equalsIgnoreCase (getDLLName()) && f.exists();
}
static void setPropertyCallback (const char* key, const char* value)
{
auto keyStr = String (key);
if (keyStr.isNotEmpty())
getGlobalProperties().setValue (key, value);
else
jassertfalse;
}
static void getPropertyCallback (const char* key, char* value, size_t size)
{
jassert (getGlobalProperties().getValue (key).getNumBytesAsUTF8() < size);
value[0] = 0;
getGlobalProperties().getValue (key).copyToUTF8 (value, size);
}
static void crashCallback (const char*) {}
static void quitCallback() {}
};

View file

@ -1,307 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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 "../Application/jucer_Application.h"
#include "jucer_CompileEngineServer.h"
#include "jucer_CompileEngineDLL.h"
#include "jucer_MessageIDs.h"
#include "jucer_CppHelpers.h"
#include "jucer_SourceCodeRange.h"
#include "jucer_ClassDatabase.h"
#include "jucer_DiagnosticMessage.h"
#include "jucer_ProjectBuildInfo.h"
#include "jucer_ClientServerMessages.h"
#if JUCE_LINUX || JUCE_BSD
#include <sys/types.h>
#include <unistd.h>
#endif
#ifndef RUN_CLANG_IN_CHILD_PROCESS
#error
#endif
#if RUN_CLANG_IN_CHILD_PROCESS
static bool parentProcessHasExited();
#endif
#if JUCE_WINDOWS
static void setParentProcessID (int);
static int getCurrentProcessID();
#else
#include <unistd.h>
#endif
//==============================================================================
/** Detects whether this process has hung, and kills it if so. */
struct ZombiePatrol : private Thread,
private AsyncUpdater,
private Timer
{
explicit ZombiePatrol (MessageHandler& mh)
: Thread ("Ping"), owner (mh)
{
startThread (2);
startTimer (1000);
}
~ZombiePatrol() override
{
stopThread (1000);
}
private:
MessageHandler& owner;
int failedPings = 0;
void run() override
{
while (! threadShouldExit())
{
#if RUN_CLANG_IN_CHILD_PROCESS
if (parentProcessHasExited())
{
killProcess();
break;
}
#endif
wait (1000);
}
}
void handleAsyncUpdate() override
{
DBG ("Server: quitting");
stopTimer();
ProjucerApplication::getApp().systemRequestedQuit();
}
void timerCallback() override
{
if (! MessageTypes::sendPing (owner))
{
if (++failedPings == 10)
{
killProcess();
return;
}
}
else
{
failedPings = 0;
}
}
void killProcess()
{
triggerAsyncUpdate(); // give the messagequeue a chance to do things cleanly.
static UnstoppableKillerThread* k = new UnstoppableKillerThread(); // (allowed to leak, but static so only one is created)
ignoreUnused (k);
}
struct UnstoppableKillerThread : public Thread
{
UnstoppableKillerThread() : Thread ("Killer") { startThread(); }
void run() override
{
wait (15000);
if (! threadShouldExit())
Process::terminate();
}
};
};
//==============================================================================
class ServerIPC : public InterprocessConnection,
public MessageHandler
{
public:
explicit ServerIPC (const StringArray& info)
: InterprocessConnection (true), liveCodeBuilder (nullptr)
{
if (! createPipe (info[0], -1))
{
Logger::writeToLog ("*** Couldn't create pipe!");
ProjucerApplication::getApp().systemRequestedQuit();
return;
}
if (dll.isLoaded())
liveCodeBuilder = dll.projucer_createBuilder (sendMessageCallback, this, info[1].toRawUTF8(), info[2].toRawUTF8());
#if JUCE_WINDOWS
setParentProcessID (info[3].getHexValue32());
#endif
zombieKiller.reset (new ZombiePatrol (*this));
}
~ServerIPC() override
{
zombieKiller.reset();
if (dll.isLoaded())
dll.projucer_deleteBuilder (liveCodeBuilder);
dll.shutdown();
DBG ("Server: finished closing down");
}
void connectionMade() override
{
DBG ("Server: client connected");
}
void connectionLost() override
{
Logger::writeToLog ("Server: client lost");
JUCEApplication::quit();
}
void sendQuitMessageToIDE()
{
MessageTypes::sendShouldCloseIDE (*this);
}
bool sendMessage (const ValueTree& m) override
{
return InterprocessConnection::sendMessage (MessageHandler::convertMessage (m));
}
void messageReceived (const MemoryBlock& message) override
{
jassert (dll.isLoaded());
dll.projucer_sendMessage (liveCodeBuilder, message.getData(), message.getSize());
}
static bool sendMessageCallback (void* userInfo, const void* data, size_t dataSize)
{
return static_cast<InterprocessConnection*> (static_cast<ServerIPC*> (userInfo))
->sendMessage (MemoryBlock (data, dataSize));
}
CompileEngineDLL dll;
LiveCodeBuilder liveCodeBuilder;
std::unique_ptr<ZombiePatrol> zombieKiller;
};
//==============================================================================
const char* commandPrefix = "--server:";
const char* commandTokenSeparator = "\x01";
String createCommandLineForLaunchingServer (const String& pipeName, const String& projectUID, const File& cacheLocation)
{
StringArray info;
info.add (pipeName);
info.add (projectUID);
info.add (cacheLocation.getFullPathName());
#if JUCE_WINDOWS
info.add (String::toHexString (getCurrentProcessID()));
#endif
const File exe (File::getSpecialLocation (File::currentExecutableFile).getFullPathName());
return "\"" + exe.getFullPathName() + "\" " + commandPrefix + info.joinIntoString (commandTokenSeparator);
}
static ServerIPC* currentServer = nullptr;
static void crashCallback (const char* message)
{
if (currentServer != nullptr)
{
#if RUN_CLANG_IN_CHILD_PROCESS
MessageTypes::sendCrash (*currentServer, message);
Logger::writeToLog ("*** Crashed! " + String (message));
#else
ignoreUnused (message);
jassertfalse;
#endif
currentServer->disconnect();
}
}
static void quitCallback()
{
ProjucerApplication::getApp().systemRequestedQuit();
}
void* createClangServer (const String& commandLine)
{
StringArray info;
info.addTokens (commandLine.fromFirstOccurrenceOf (commandPrefix, false, false), commandTokenSeparator, "");
std::unique_ptr<ServerIPC> ipc (new ServerIPC (info));
if (ipc->dll.isLoaded())
{
ipc->dll.initialise (crashCallback, quitCallback, (bool) RUN_CLANG_IN_CHILD_PROCESS);
currentServer = ipc.release();
return currentServer;
}
return nullptr;
}
void destroyClangServer (void* server)
{
currentServer = nullptr;
delete static_cast<ServerIPC*> (server);
}
void sendQuitMessageToIDE (void* server)
{
static_cast<ServerIPC*> (server)->sendQuitMessageToIDE();
}
//==============================================================================
#if JUCE_WINDOWS
#define STRICT 1
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
static HANDLE parentProcessHandle = nullptr;
static void setParentProcessID (int pid) { parentProcessHandle = OpenProcess (SYNCHRONIZE, FALSE, (DWORD) pid); }
static int getCurrentProcessID() { return (int) GetCurrentProcessId(); }
#endif
#if RUN_CLANG_IN_CHILD_PROCESS
bool parentProcessHasExited()
{
#if JUCE_WINDOWS
return WaitForSingleObject (parentProcessHandle, 0) == WAIT_OBJECT_0;
#else
return getppid() == 1;
#endif
}
#endif

View file

@ -1,42 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
//==============================================================================
// These functions are called by our child process on startup, to launch
// the compilation server
String createCommandLineForLaunchingServer (const String& pipeName,
const String& projectUID,
const File& cacheLocation);
void* createClangServer (const String& commandLine);
void destroyClangServer (void*);
// Called if our child process is asked to shutdown by the user, so it can pass
// that shutdown event up to the parent (IDE) process..
void sendQuitMessageToIDE (void*);

View file

@ -1,109 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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 CompileEngineSettings
{
public:
CompileEngineSettings (ValueTree& projectRoot)
: tree (projectRoot.getOrCreateChildWithName ("LIVE_SETTINGS", nullptr)
.getOrCreateChildWithName (getLiveSettingsSubType(), nullptr)),
buildEnabledValue (tree, Ids::buildEnabled, nullptr, false),
continuousRebuildEnabledValue (tree, Ids::continuousRebuildEnabled, nullptr, false),
warningsEnabledValue (tree, Ids::warningsEnabled, nullptr, true),
userHeaderPathValue (tree, Ids::headerPath, nullptr),
systemHeaderPathValue (tree, Ids::systemHeaderPath, nullptr),
extraDLLsValue (tree, Ids::extraDLLs, nullptr),
extraCompilerFlagsValue (tree, Ids::extraCompilerFlags, nullptr),
extraPreprocessorDefsValue (tree, Ids::defines, nullptr),
windowsTargetPlatformValue (tree, Ids::windowsTargetPlatformVersion, nullptr, "10.0.16299.0")
{
}
//==============================================================================
void setBuildEnabled (bool enabled) noexcept { buildEnabledValue = enabled; }
void setContinuousRebuildEnabled (bool enabled) noexcept { continuousRebuildEnabledValue = enabled; }
void setWarningsEnabled (bool enabled) { warningsEnabledValue = enabled; }
bool isBuildEnabled() const noexcept { return buildEnabledValue.get(); }
bool isContinuousRebuildEnabled() const noexcept { return continuousRebuildEnabledValue.get(); }
bool areWarningsEnabled() const noexcept { return warningsEnabledValue.get(); }
String getUserHeaderPathString() const noexcept { return userHeaderPathValue.get(); }
String getSystemHeaderPathString() const noexcept { return systemHeaderPathValue.get(); }
String getExtraDLLsString() const noexcept { return extraDLLsValue.get(); }
String getExtraCompilerFlagsString() const noexcept { return extraCompilerFlagsValue.get(); }
String getExtraPreprocessorDefsString() const noexcept { return extraPreprocessorDefsValue.get(); }
String getWindowsTargetPlatformVersionString() const noexcept { return windowsTargetPlatformValue.get(); }
//==============================================================================
void getLiveSettings (PropertyListBuilder& props)
{
props.addSearchPathProperty (userHeaderPathValue, "User Header Paths", "User header search paths.");
props.addSearchPathProperty (systemHeaderPathValue, "System Header Paths", "System header search paths.");
props.add (new TextPropertyComponent (extraPreprocessorDefsValue, "Preprocessor Definitions", 32768, true),
"Extra preprocessor definitions. Use the form \"NAME1=value NAME2=value\", using whitespace or commas "
"to separate the items - to include a space or comma in a definition, precede it with a backslash.");
props.add (new TextPropertyComponent (extraCompilerFlagsValue, "Extra Compiler Flags", 2048, true),
"Extra command-line flags to be passed to the compiler. This string can contain references to preprocessor"
" definitions in the form ${NAME_OF_DEFINITION}, which will be replaced with their values.");
props.add (new TextPropertyComponent (extraDLLsValue, "Extra Dynamic Libraries", 2048, true),
"Extra dynamic libs that the running code may require. Use new-lines or commas to separate the items.");
props.add (new TextPropertyComponent (windowsTargetPlatformValue, "Windows Target Platform", 256, false),
"The Windows target platform to use.");
}
private:
ValueTree tree;
ValueWithDefault buildEnabledValue, continuousRebuildEnabledValue, warningsEnabledValue, userHeaderPathValue, systemHeaderPathValue,
extraDLLsValue, extraCompilerFlagsValue, extraPreprocessorDefsValue, windowsTargetPlatformValue;
//==============================================================================
String getLiveSettingsSubType() const noexcept
{
#if JUCE_MAC
return "OSX";
#elif JUCE_WINDOWS
return "WINDOWS";
#elif JUCE_LINUX || JUCE_BSD
return "LINUX";
#else
// unknown platform?!
jassertfalse;
return {};
#endif
}
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CompileEngineSettings)
};

View file

@ -1,283 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
//==============================================================================
struct CppParserHelpers
{
static bool parseHexInt (const String& text, int64& result)
{
CppTokeniserFunctions::StringIterator i (text);
if (CppTokeniserFunctions::parseHexLiteral (i))
{
result = text.fromFirstOccurrenceOf ("x", false, true).getHexValue64();
return true;
}
return false;
}
static bool parseOctalInt (const String& text, int64& result)
{
CppTokeniserFunctions::StringIterator it (text);
if (CppTokeniserFunctions::parseOctalLiteral (it))
{
result = 0;
for (int i = 0; i < text.length(); ++i)
{
const auto digit = (int) (text[i] - '0');
if (digit < 0 || digit > 7)
break;
result = result * 8 + digit;
}
return true;
}
return false;
}
static bool parseDecimalInt (const String& text, int64& result)
{
CppTokeniserFunctions::StringIterator i (text);
if (CppTokeniserFunctions::parseDecimalLiteral (i))
{
result = text.getLargeIntValue();
return true;
}
return false;
}
static bool parseInt (const String& text, int64& result)
{
return parseHexInt (text, result)
|| parseOctalInt (text, result)
|| parseDecimalInt (text, result);
}
static bool parseFloat (const String& text, double& result)
{
CppTokeniserFunctions::StringIterator i (text);
if (CppTokeniserFunctions::parseFloatLiteral (i))
{
result = text.getDoubleValue();
return true;
}
return false;
}
static int parseSingleToken (const String& text)
{
if (text.isEmpty())
return CPlusPlusCodeTokeniser::tokenType_error;
CppTokeniserFunctions::StringIterator i (text);
i.skipWhitespace();
const int tok = CppTokeniserFunctions::readNextToken (i);
i.skipWhitespace();
i.skip();
return i.isEOF() ? tok : CPlusPlusCodeTokeniser::tokenType_error;
}
static String getIntegerSuffix (const String& s) { return s.retainCharacters ("lLuU"); }
static String getFloatSuffix (const String& s) { return s.retainCharacters ("fF"); }
static String getReplacementStringInSameFormat (const String& old, double newValue)
{
{
CppTokeniserFunctions::StringIterator i (old);
if (CppTokeniserFunctions::parseFloatLiteral (i))
{
String s (newValue);
if (! s.containsChar ('.'))
s += ".0";
return s + getFloatSuffix (old);
}
}
return getReplacementStringInSameFormat (old, (int64) newValue);
}
static String getReplacementStringInSameFormat (const String& old, int64 newValue)
{
{
CppTokeniserFunctions::StringIterator i (old);
if (CppTokeniserFunctions::parseHexLiteral (i))
{
String s ("0x" + String::toHexString (newValue) + getIntegerSuffix (old));
if (old.toUpperCase() == old)
s = s.toUpperCase();
return s;
}
}
{
CppTokeniserFunctions::StringIterator i (old);
if (CppTokeniserFunctions::parseDecimalLiteral (i))
return String (newValue) + getIntegerSuffix (old);
}
return old;
}
// Given a type name which could be a smart pointer or other pointer/ref, this extracts
// the essential class name of the thing that it points to.
static String getSignificantClass (String cls)
{
int firstAngleBracket = cls.indexOfChar ('<');
if (firstAngleBracket > 0)
cls = cls.substring (firstAngleBracket + 1).upToLastOccurrenceOf (">", false, false).trim();
while (cls.endsWithChar ('*') || cls.endsWithChar ('&'))
cls = cls.dropLastCharacters (1).trim();
return cls;
}
//==============================================================================
struct ValidCppIdentifierRestriction : public TextEditor::InputFilter
{
ValidCppIdentifierRestriction (bool allowTemplatesAndNamespaces)
: className (allowTemplatesAndNamespaces) {}
String filterNewText (TextEditor& ed, const String& text)
{
String allowedChars ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_");
if (className)
allowedChars += "<>:";
if (ed.getHighlightedRegion().getStart() > 0)
allowedChars += "0123456789";
String s = text.retainCharacters (allowedChars);
if (CPlusPlusCodeTokeniser::isReservedKeyword (ed.getText().replaceSection (ed.getHighlightedRegion().getStart(),
ed.getHighlightedRegion().getLength(),
s)))
return String();
return s;
}
bool className;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValidCppIdentifierRestriction)
};
};
//==============================================================================
struct CodeChange
{
CodeChange (Range<int> r, const String& t) : range (r), text (t)
{
}
bool mergeWith (const CodeChange& next)
{
if (text.isEmpty())
{
if (next.text.isNotEmpty()
&& next.range.isEmpty()
&& next.range.getStart() == range.getStart())
{
text = next.text;
return true;
}
if (next.text.isEmpty())
{
Range<int> nextRange (next.range);
if (nextRange.getStart() >= range.getStart())
nextRange += range.getLength();
else if (nextRange.getEnd() > range.getStart())
nextRange.setEnd (nextRange.getEnd() + range.getLength());
if (range.intersects (nextRange)
|| range.getEnd() == nextRange.getStart()
|| range.getStart() == nextRange.getEnd())
{
range = range.getUnionWith (nextRange);
return true;
}
}
}
else if (next.text.isEmpty())
{
if (next.range.getEnd() == range.getStart())
{
range.setStart (next.range.getStart());
return true;
}
if (next.range.getStart() == range.getStart() + text.length())
{
range.setLength (range.getLength() + next.range.getLength());
return true;
}
}
return false;
}
void addToList (Array<CodeChange>& list) const
{
if (list.size() == 0 || ! list.getReference (list.size() - 1).mergeWith (*this))
list.add (*this);
}
Range<int> range;
String text;
};
//==============================================================================
static inline String concatenateListOfStrings (const StringArray& s)
{
return s.joinIntoString ("\x01");
}
static inline StringArray separateJoinedStrings (const String& s)
{
return StringArray::fromTokens (s, "\x01", juce::StringRef());
}

View file

@ -1,208 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
//==============================================================================
struct DiagnosticMessage
{
DiagnosticMessage() = default;
DiagnosticMessage (const DiagnosticMessage& other)
: associatedDiagnostic (createCopyIfNotNull (other.associatedDiagnostic.get())),
message (other.message),
mainFile (other.mainFile),
range (other.range),
type (other.type)
{
}
DiagnosticMessage& operator= (const DiagnosticMessage& other)
{
associatedDiagnostic.reset (createCopyIfNotNull (other.associatedDiagnostic.get()));
message = other.message;
mainFile = other.mainFile;
range = other.range;
type = other.type;
return *this;
}
enum Type
{
error = 0,
warning = 1,
note = 2
};
std::unique_ptr<DiagnosticMessage> associatedDiagnostic;
String message;
String mainFile;
SourceCodeRange range;
Type type;
bool isError() const noexcept { return type == error; }
bool isWarning() const noexcept { return type == warning; }
bool isNote() const noexcept { return type == note; }
String toString() const
{
// todo: copy recursively from root
String res;
switch (type)
{
case error: res << "error: "; break;
case warning: res << "warning: "; break;
case note: res << "note: "; break;
default: break;
};
res << mainFile << ": ";
res << message << "\n";
return res;
}
ValueTree toValueTree() const
{
ValueTree v (MessageTypes::DIAGNOSTIC);
v.setProperty (Ids::text, message, nullptr);
v.setProperty (Ids::file, mainFile, nullptr);
v.setProperty (Ids::range, range.toString(), nullptr);
v.setProperty (Ids::type, (int) type, nullptr);
if (associatedDiagnostic != nullptr)
v.addChild (associatedDiagnostic->toValueTree(), 0, nullptr);
return v;
}
static DiagnosticMessage fromValueTree (const ValueTree& v)
{
DiagnosticMessage d;
d.message = v[Ids::text];
d.mainFile = v[Ids::file];
d.range = SourceCodeRange (v [Ids::range]);
d.type = (Type) static_cast<int> (v[Ids::type]);
auto associated = v.getChild (0);
if (associated.isValid())
d.associatedDiagnostic.reset (new DiagnosticMessage (fromValueTree (associated)));
return d;
}
bool operator== (const DiagnosticMessage& other) const noexcept
{
return range == other.range
&& message == other.message
&& mainFile == other.mainFile;
}
bool operator!= (const DiagnosticMessage& other) const noexcept { return ! operator== (other); }
};
//==============================================================================
struct DiagnosticList
{
// after some research, it seems that notes never come on their own
// i.e. they always have a warning / error preceding them
// so we can keep notes and their associated diagnostics
// together by keeping track of the last message
DiagnosticMessage lastMessage;
ValueTree list { MessageTypes::DIAGNOSTIC_LIST };
void clear()
{
list = ValueTree { MessageTypes::DIAGNOSTIC_LIST };
lastMessage = DiagnosticMessage();
}
void add (DiagnosticMessage m)
{
if (m.isNote())
{
if (lastMessage.message.isEmpty())
return; // seems to happen sometimes, but with seemingly duplicated messages (?)
m.associatedDiagnostic.reset (new DiagnosticMessage (lastMessage));
}
else
{
lastMessage = m;
}
list.appendChild (m.toValueTree(), nullptr);
}
void add (const DiagnosticList& l)
{
jassert (l.list != list);
for (int i = 0; i < l.list.getNumChildren(); ++i)
list.appendChild (l.list.getChild(i).createCopy(), nullptr);
}
void remove (DiagnosticMessage m)
{
auto n = m.toValueTree();
for (int i = 0; i < list.getNumChildren(); ++i)
{
if (list.getChild (i).isEquivalentTo (n))
{
list.removeChild (i, nullptr);
return;
}
}
jassertfalse;
}
bool hasRecoveryWarning (DiagnosticMessage m) const
{
auto n = m.toValueTree();
for (int i = 0; i < list.getNumChildren(); ++i)
if (list.getChild (i).isEquivalentTo (n))
return true;
return false;
}
const ValueTree& toValueTree() const noexcept
{
return list;
}
void loadFromChildOfValueTree (ValueTree& parent)
{
list = parent.getChildWithName (MessageTypes::DIAGNOSTIC_LIST).createCopy();
}
};

View file

@ -1,166 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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_DownloadCompileEngineThread.h"
#include "../LiveBuildEngine/jucer_CompileEngineDLL.h"
#include "../Utility/Helpers/jucer_VersionInfo.h"
//==============================================================================
bool DownloadCompileEngineThread::downloadAndInstall()
{
DownloadCompileEngineThread d;
if (d.runThread())
{
if (d.result.failed())
return withError (d.result.getErrorMessage());
return true;
}
if (d.cancelledByUser)
return false;
return withError (d.result.getErrorMessage());
}
DownloadCompileEngineThread::DownloadCompileEngineThread()
: ThreadWithProgressWindow ("Downloading live-build engine", true, true),
result (Result::ok()), cancelledByUser (false)
{
}
void DownloadCompileEngineThread::threadComplete (bool userPressedCancel)
{
cancelledByUser = userPressedCancel;
}
void DownloadCompileEngineThread::run()
{
setProgress (-1.0);
setStatusMessage ("Downloading...");
MemoryBlock zipData;
result = download (zipData);
if (result.failed())
return;
setStatusMessage ("Installing...");
File installFolder = getInstallFolder();
if (! installFolder.createDirectory())
{
result = Result::fail ("Install error: cannot create target directory");
return;
}
result = install (zipData, installFolder);
}
Result DownloadCompileEngineThread::download (MemoryBlock& dest)
{
auto info = VersionInfo::fetchFromUpdateServer (ProjectInfo::versionString);
if (info == nullptr)
return Result::fail ("Download error: cannot communicate with server");
auto requiredAssetName = []
{
String name ("JUCECompileEngine_");
#if JUCE_MAC
name << "osx_";
#elif JUCE_WINDOWS
name << "windows_";
#else
jassertfalse;
#endif
return name + ProjectInfo::versionString + ".zip";
}();
for (auto& asset : info->assets)
{
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");
bytesReceived = mo.writeFromInputStream (*in, 8192);
if (bytesReceived == 0)
return Result::fail ("Download error: lost connection");
}
return Result::ok();
}
}
return Result::fail ("Download error: no downloads available");
}
Result DownloadCompileEngineThread::install (const MemoryBlock& data, File& targetFolder)
{
MemoryInputStream input (data, false);
ZipFile zip (input);
if (zip.getNumEntries() == 0)
return Result::fail ("Install error: downloaded file is corrupt");
if (threadShouldExit())
return Result::fail ("Install error: operation interrupted");
return zip.uncompressTo (targetFolder);
}
File DownloadCompileEngineThread::getInstallFolder()
{
return CompileEngineDLL::getVersionedUserAppSupportFolder();
}
bool DownloadCompileEngineThread::withError(const String& msg)
{
AlertWindow::showMessageBox (AlertWindow::WarningIcon,
"Download and install", msg);
return false;
}

View file

@ -1,53 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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 DownloadCompileEngineThread : public ThreadWithProgressWindow
{
public:
static bool downloadAndInstall();
protected:
void run() override;
void threadComplete (bool userPressedCancel) override;
private:
DownloadCompileEngineThread();
Result download (MemoryBlock& dest);
Result install (const MemoryBlock& data, File& targetFolder);
static URL getDownloadUrl();
static File getInstallFolder();
static bool withError(const String& msg);
Result result;
bool cancelledByUser;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DownloadCompileEngineThread)
};

View file

@ -1,120 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
//==============================================================================
struct ErrorList : public ChangeBroadcaster
{
ErrorList() : warningsEnabled (true) {}
void takeCopy (Array<DiagnosticMessage>& dest) const
{
checkThread();
if (warningsEnabled)
{
dest = messages;
}
else
{
for (auto& d : messages)
if (d.isError())
dest.add (d);
}
}
void resetToError (const String& message)
{
DiagnosticMessage m;
m.message = message;
m.type = DiagnosticMessage::error;
DiagnosticList list;
list.add (m);
setList (list.toValueTree());
}
void setList (const ValueTree& newList)
{
checkThread();
messages.clear();
for (int i = 0; i < newList.getNumChildren(); ++i)
messages.add (DiagnosticMessage::fromValueTree (newList.getChild(i)));
sendChangeMessage();
}
bool isEmpty() const noexcept { return messages.size() == 0; }
int getNumErrors() const
{
checkThread();
int num = 0;
for (const auto& m : messages)
if (m.isError())
++num;
return num;
}
int getNumWarnings() const
{
checkThread();
int num = 0;
for (const auto& m : messages)
if (m.isWarning())
++num;
return num;
}
void setWarningsEnabled (bool enabled)
{
if (warningsEnabled != enabled)
{
warningsEnabled = enabled;
if (messages.size() > 0)
sendChangeMessage();
}
}
private:
Array<DiagnosticMessage> messages;
bool warningsEnabled;
static void checkThread()
{
JUCE_ASSERT_MESSAGE_THREAD
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ErrorList)
};

View file

@ -1,50 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
extern "C"
{
typedef void* LiveCodeBuilder;
typedef bool (*SendMessageFunction) (void* userInfo, const void* data, size_t dataSize);
typedef void (*CrashCallbackFunction) (const char* crashDescription);
typedef void (*QuitCallbackFunction)();
typedef void (*SetPropertyFunction) (const char* key, const char* value);
typedef void (*GetPropertyFunction) (const char* key, char* value, size_t size);
// We've used an X macro to define the DLL functions rather than just declaring them, so that
// we can load the DLL and its functions dynamically and cope with it not being there.
// The CompileEngineDLL class is a wrapper that manages finding/loading the DLL and exposing
// these as callable functions.
#define LIVE_DLL_FUNCTIONS(X) \
X (projucer_getVersion, int, ()) \
X (projucer_initialise, void, (CrashCallbackFunction, QuitCallbackFunction, SetPropertyFunction, GetPropertyFunction, bool setupSignals)) \
X (projucer_shutdown, void, ()) \
X (projucer_createBuilder, LiveCodeBuilder, (SendMessageFunction, void* userInfo, const char* projectID, const char* cacheFolder)) \
X (projucer_sendMessage, void, (LiveCodeBuilder, const void* messageData, size_t messageDataSize)) \
X (projucer_deleteBuilder, void, (LiveCodeBuilder))
}

View file

@ -1,63 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
namespace MessageTypes
{
#define DECLARE_ID(name) const Identifier name (#name)
DECLARE_ID (PING);
DECLARE_ID (BUILDINFO);
DECLARE_ID (COMPILEUNIT);
DECLARE_ID (USERFILE);
DECLARE_ID (DIAGNOSTIC);
DECLARE_ID (DIAGNOSTIC_LIST);
DECLARE_ID (ACTIVITY_LIST);
DECLARE_ID (MISSING_SYSTEM_HEADERS);
DECLARE_ID (BUILD_FAILED);
DECLARE_ID (CHANGE_CODE);
DECLARE_ID (HIGHLIGHT_CODE);
DECLARE_ID (CRASH);
DECLARE_ID (LAUNCHED);
DECLARE_ID (APPQUIT);
DECLARE_ID (KEY);
DECLARE_ID (QUIT_IDE);
DECLARE_ID (CLEAN_ALL);
DECLARE_ID (OPEN_PREVIEW);
DECLARE_ID (RELOAD);
DECLARE_ID (LIVE_FILE_CHANGES);
DECLARE_ID (CHANGE);
DECLARE_ID (LIVE_FILE_UPDATE);
DECLARE_ID (LIVE_FILE_RESET);
DECLARE_ID (LAUNCH_APP);
DECLARE_ID (FOREGROUND);
DECLARE_ID (QUIT_SERVER);
#undef DECLARE_ID
}

View file

@ -1,101 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
//==============================================================================
struct ProjectBuildInfo
{
ProjectBuildInfo() : tree (MessageTypes::BUILDINFO) {}
ProjectBuildInfo (const ValueTree& t) : tree (t) {}
Array<File> getCompileUnits() const
{
Array<File> files;
for (int i = 0; i < tree.getNumChildren(); ++i)
if (tree.getChild(i).hasType (MessageTypes::COMPILEUNIT))
files.add (File (tree.getChild(i) [Ids::file].toString()));
return files;
}
// This is a list of all cpp and header files that are actually "user" code
// rather than system or internal files
Array<File> getUserFiles() const
{
Array<File> files;
for (int i = 0; i < tree.getNumChildren(); ++i)
if (tree.getChild(i).hasType (MessageTypes::USERFILE))
files.add (File (tree.getChild(i) [Ids::file].toString()));
return files;
}
void setFiles (const Array<File>& compileUnits, const Array<File>& allUserFiles)
{
for (const File& f : compileUnits)
{
ValueTree file (MessageTypes::COMPILEUNIT);
file.setProperty (Ids::file, f.getFullPathName(), nullptr);
tree.appendChild (file, nullptr);
}
for (const File& f : allUserFiles)
{
ValueTree file (MessageTypes::USERFILE);
file.setProperty (Ids::file, f.getFullPathName(), nullptr);
tree.appendChild (file, nullptr);
}
}
StringArray getSystemIncludes() const { return separateJoinedStrings (tree [Ids::systempath]); }
StringArray getUserIncludes() const { return separateJoinedStrings (tree [Ids::userpath]); }
void setSystemIncludes (const StringArray& s) { tree.setProperty (Ids::systempath, concatenateListOfStrings (s), nullptr); }
void setUserIncludes (const StringArray& s) { tree.setProperty (Ids::userpath, concatenateListOfStrings (s), nullptr); }
String getGlobalDefs() const { return tree [Ids::defines]; }
void setGlobalDefs (const String& defs) { tree.setProperty (Ids::defines, defs, nullptr); }
String getCompileFlags() const { return tree [Ids::extraCompilerFlags]; }
void setCompileFlags (const String& f) { tree.setProperty (Ids::extraCompilerFlags, f, nullptr); }
String getUtilsCppInclude() const { return tree [Ids::utilsCppInclude]; }
void setUtilsCppInclude (const String& s) { tree.setProperty (Ids::utilsCppInclude, s, nullptr); }
String getJuceModulesFolder() const { return tree [Ids::juceModulesFolder]; }
void setJuceModulesFolder (const String& s) { tree.setProperty (Ids::juceModulesFolder, s, nullptr); }
StringArray getExtraDLLs() const { return separateJoinedStrings (tree [Ids::extraDLLs]); }
void setExtraDLLs (const StringArray& s) { tree.setProperty (Ids::extraDLLs, concatenateListOfStrings (s), nullptr); }
String getWindowsTargetPlatformVersion() const { return tree [Ids::liveWindowsTargetPlatformVersion]; }
void setWindowsTargetPlatformVersion (const String& s) { tree.setProperty (Ids::liveWindowsTargetPlatformVersion, s, nullptr); }
ValueTree tree;
};

View file

@ -1,105 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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
//==============================================================================
struct SourceCodeRange
{
SourceCodeRange() = default;
SourceCodeRange (const String& f, int start, int end)
: file (f), range (start, end)
{
#if JUCE_WINDOWS
file = file.replaceCharacter ('/', '\\');
#endif
}
SourceCodeRange (const String& s)
{
String::CharPointerType colon1 (nullptr), colon2 (nullptr);
for (auto p = s.getCharPointer(); ! p.isEmpty(); ++p)
{
if (*p == ':')
{
colon1 = colon2;
colon2 = p;
}
}
if (colon1.getAddress() != nullptr && colon2.getAddress() != nullptr)
{
file = String (s.getCharPointer(), colon1);
range = Range<int> ((colon1 + 1).getIntValue32(),
(colon2 + 1).getIntValue32());
}
}
String file;
Range<int> range;
bool isValid() const noexcept { return file.isNotEmpty() && range != Range<int>(); }
void nudge (const String& changedFile, const int insertPoint, const int delta) noexcept
{
if (range.getEnd() >= insertPoint && file == changedFile)
{
const int newEnd = range.getEnd() + delta;
int newStart = range.getStart();
if (newStart > insertPoint)
newStart += delta;
range = Range<int> (newStart, newEnd);
}
}
void fileContentChanged (const String& changedFile) noexcept
{
if (file == changedFile)
range = Range<int>();
}
String toString() const
{
if (file.isEmpty() && range.isEmpty())
return String();
return file + ":" + String (range.getStart()) + ":" + String (range.getEnd());
}
void writeToValueTree (ValueTree& v, const Identifier& prop) const
{
const String s (toString());
if (s.isNotEmpty())
v.setProperty (prop, s, nullptr);
}
bool operator== (const SourceCodeRange& other) const noexcept { return range == other.range && file == other.file; }
bool operator!= (const SourceCodeRange& other) const noexcept { return ! operator== (other); }
};