|
- /*
- ==============================================================================
-
- This file is part of the JUCE library - "Jules' Utility Class Extensions"
- Copyright 2004-10 by Raw Material Software Ltd.
-
- ------------------------------------------------------------------------------
-
- JUCE can be redistributed and/or modified under the terms of the GNU General
- Public License (Version 2), as published by the Free Software Foundation.
- A copy of the license is included in the JUCE distribution, or can be found
- online at www.gnu.org/licenses.
-
- JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- ------------------------------------------------------------------------------
-
- To release a closed-source product which uses JUCE, commercial licenses are
- available: visit www.rawmaterialsoftware.com/juce for more information.
-
- ==============================================================================
- */
-
- #ifndef __JUCER_PROJECTSAVER_JUCEHEADER__
- #define __JUCER_PROJECTSAVER_JUCEHEADER__
-
-
- //==============================================================================
- class ProjectSaver
- {
- public:
- ProjectSaver (Project& project_, const File& projectFile_)
- : project (project_), projectFile (projectFile_), resourceFile (project_),
- generatedCodeFolder (project.getGeneratedCodeFolder())
- {
- }
-
- String save()
- {
- const File oldFile (project.getFile());
- project.setFile (projectFile);
-
- const String linkageMode (project.getJuceLinkageMode());
-
- if (linkageMode == Project::notLinkedToJuce)
- {
- hasAppHeaderFile = ! project.getProjectType().isLibrary();
- numJuceSourceFiles = 0;
- }
- else if (linkageMode == Project::useAmalgamatedJuce
- || linkageMode == Project::useAmalgamatedJuceViaSingleTemplate)
- {
- hasAppHeaderFile = true;
- numJuceSourceFiles = 1;
- }
- else if (linkageMode == Project::useAmalgamatedJuceViaMultipleTemplates)
- {
- hasAppHeaderFile = true;
- numJuceSourceFiles = project.getNumSeparateAmalgamatedFiles();
- }
- else if (linkageMode == Project::useLinkedJuce)
- {
- hasAppHeaderFile = true;
- numJuceSourceFiles = 0;
- }
- else
- {
- jassertfalse;
- }
-
- hasResources = (resourceFile.getNumFiles() > 0);
-
- writeMainProjectFile();
-
- if (! generatedCodeFolder.createDirectory())
- errors.add ("Couldn't create folder: " + generatedCodeFolder.getFullPathName());
-
- if (errors.size() == 0)
- writeAppConfigFile();
-
- if (errors.size() == 0)
- writeJuceSourceWrappers();
-
- if (errors.size() == 0)
- writeProjects();
-
- if (errors.size() > 0)
- project.setFile (oldFile);
-
- return errors[0];
- }
-
- bool saveGeneratedFile (const String& filePath, const MemoryOutputStream& newData)
- {
- return replaceFileIfDifferent (generatedCodeFolder.getChildFile (filePath), newData);
- }
-
- private:
- Project& project;
- const File& projectFile;
- ResourceFile resourceFile;
- File generatedCodeFolder;
- StringArray errors;
-
- File appConfigFile, juceHeaderFile, binaryDataCpp, pluginCharacteristicsFile;
- bool hasAppHeaderFile, hasResources;
- int numJuceSourceFiles;
-
- void writeMainProjectFile()
- {
- ScopedPointer <XmlElement> xml (project.getProjectRoot().createXml());
- jassert (xml != nullptr);
-
- if (xml != nullptr)
- {
- #if JUCE_DEBUG
- {
- MemoryOutputStream mo;
- project.getProjectRoot().writeToStream (mo);
-
- MemoryInputStream mi (mo.getData(), mo.getDataSize(), false);
- ValueTree v = ValueTree::readFromStream (mi);
- ScopedPointer <XmlElement> xml2 (v.createXml());
-
- // This bit just tests that ValueTree save/load works reliably.. Let me know if this asserts for you!
- jassert (xml->isEquivalentTo (xml2, true));
- }
- #endif
-
- MemoryOutputStream mo;
- xml->writeToStream (mo, String::empty);
-
- if (! FileHelpers::overwriteFileWithNewDataIfDifferent (projectFile, mo))
- errors.add ("Couldn't write to the target file!");
- }
- }
-
- static void writeJucerComment (OutputStream& out)
- {
- out << "/*" << newLine << newLine
- << " IMPORTANT! This file is auto-generated each time you save your" << newLine
- << " project - if you alter its contents, your changes may be overwritten!" << newLine
- << newLine;
- }
-
- bool writeAppConfig (OutputStream& out)
- {
- writeJucerComment (out);
- out << " If you want to change any of these values, use the Introjucer to do so, rather than" << newLine
- << " editing this file directly!" << newLine
- << newLine
- << " Any commented-out settings will fall back to using the default values that" << newLine
- << " they are given in juce_Config.h" << newLine
- << newLine
- << "*/" << newLine << newLine;
-
- bool notActive = project.getJuceLinkageMode() == Project::useLinkedJuce
- || project.getJuceLinkageMode() == Project::notLinkedToJuce;
- if (notActive)
- out << "/* NOTE: These configs aren't available when you're linking to the juce library statically!" << newLine
- << " If you need to set a configuration that differs from the default, you'll need" << newLine
- << " to include the amalgamated Juce files." << newLine << newLine;
-
- OwnedArray <Project::ConfigFlag> flags;
- project.getAllConfigFlags (flags);
-
- for (int i = 0; i < flags.size(); ++i)
- {
- const Project::ConfigFlag* const f = flags[i];
- const String value (f->value.toString());
-
- if (value != Project::configFlagEnabled && value != Project::configFlagDisabled)
- out << "//#define ";
- else
- out << "#define ";
-
- out << f->symbol;
-
- if (value == Project::configFlagEnabled)
- out << " 1";
- else if (value == Project::configFlagDisabled)
- out << " 0";
-
- out << newLine;
- }
-
- if (notActive)
- out << newLine << "*/" << newLine;
-
- return flags.size() > 0;
- }
-
- void writeSourceWrapper (OutputStream& out, int fileNumber)
- {
- writeJucerComment (out);
- out << " This file pulls in all the Juce source code, and builds it using the settings" << newLine
- << " defined in " << appConfigFile.getFileName() << "." << newLine
- << newLine
- << " If you want to change the method by which Juce is linked into your app, use the" << newLine
- << " Jucer to change it, rather than trying to edit this file directly." << newLine
- << newLine
- << "*/"
- << newLine << newLine
- << CodeHelpers::createIncludeStatement (appConfigFile, appConfigFile) << newLine;
-
- if (fileNumber == 0)
- writeInclude (out, project.isUsingFullyAmalgamatedFile() ? "juce_amalgamated.cpp"
- : "amalgamation/juce_amalgamated_template.cpp");
- else
- writeInclude (out, "amalgamation/juce_amalgamated" + String (fileNumber) + ".cpp");
- }
-
- void writeAppHeader (OutputStream& out)
- {
- writeJucerComment (out);
- out << " This is the header file that your files should include in order to get all the" << newLine
- << " Juce library headers. You should NOT include juce.h or juce_amalgamated.h directly in" << newLine
- << " your own source files, because that wouldn't pick up the correct Juce configuration" << newLine
- << " options for your app." << newLine
- << newLine
- << "*/" << newLine << newLine;
-
- String headerGuard ("__APPHEADERFILE_" + String::toHexString (juceHeaderFile.hashCode()).toUpperCase() + "__");
- out << "#ifndef " << headerGuard << newLine
- << "#define " << headerGuard << newLine << newLine;
-
- if (appConfigFile.exists())
- out << CodeHelpers::createIncludeStatement (appConfigFile, appConfigFile) << newLine;
-
- if (project.getJuceLinkageMode() != Project::notLinkedToJuce)
- {
- writeInclude (out, (project.isUsingSingleTemplateFile() || project.isUsingMultipleTemplateFiles())
- ? "juce_amalgamated.h" // could use "amalgamation/juce_amalgamated_template.h", but it's slower..
- : (project.isUsingFullyAmalgamatedFile()
- ? "juce_amalgamated.h"
- : "juce.h"));
- }
-
- if (binaryDataCpp.exists())
- out << CodeHelpers::createIncludeStatement (binaryDataCpp.withFileExtension (".h"), appConfigFile) << newLine;
-
- out << newLine
- << "namespace ProjectInfo" << newLine
- << "{" << newLine
- << " const char* const projectName = " << CodeHelpers::addEscapeChars (project.getProjectName().toString()).quoted() << ";" << newLine
- << " const char* const versionString = " << CodeHelpers::addEscapeChars (project.getVersion().toString()).quoted() << ";" << newLine
- << " const int versionNumber = " << project.getVersionAsHex() << ";" << newLine
- << "}" << newLine
- << newLine
- << "#endif // " << headerGuard << newLine;
- }
-
- void writeInclude (OutputStream& out, const String& pathFromJuceFolder)
- {
- StringArray paths, guards;
-
- for (int i = project.getNumExporters(); --i >= 0;)
- {
- ScopedPointer <ProjectExporter> exporter (project.createExporter (i));
-
- if (exporter != nullptr)
- {
- paths.add (exporter->getIncludePathForFileInJuceFolder (pathFromJuceFolder, juceHeaderFile));
- guards.add ("defined (" + exporter->getExporterIdentifierMacro() + ")");
- }
- }
-
- StringArray uniquePaths (paths);
- uniquePaths.removeDuplicates (false);
-
- if (uniquePaths.size() == 1)
- {
- out << "#include " << paths[0] << newLine;
- }
- else
- {
- int i = paths.size();
- for (; --i >= 0;)
- {
- for (int j = i; --j >= 0;)
- {
- if (paths[i] == paths[j] && guards[i] == guards[j])
- {
- paths.remove (i);
- guards.remove (i);
- }
- }
- }
-
- for (i = 0; i < paths.size(); ++i)
- {
- out << (i == 0 ? "#if " : "#elif ") << guards[i] << newLine
- << " #include " << paths[i] << newLine;
- }
-
- out << "#endif" << newLine;
- }
- }
-
- static int countMaxPluginChannels (const String& configString, bool isInput)
- {
- StringArray configs;
- configs.addTokens (configString, ", {}", String::empty);
- configs.trim();
- configs.removeEmptyStrings();
- jassert ((configs.size() & 1) == 0); // looks like a syntax error in the configs?
-
- int maxVal = 0;
- for (int i = (isInput ? 0 : 1); i < configs.size(); i += 2)
- maxVal = jmax (maxVal, configs[i].getIntValue());
-
- return maxVal;
- }
-
- static void writePluginCharacteristics (const File& destFile, Project& project, OutputStream& out)
- {
- String headerGuard ("__PLUGINCHARACTERISTICS_" + String::toHexString (destFile.hashCode()).toUpperCase() + "__");
-
- writeJucerComment (out);
- out << " This header file contains configuration options for the plug-in. If you need to change any of" << newLine
- << " these, it'd be wise to do so using the Jucer, rather than editing this file directly..." << newLine
- << newLine
- << "*/" << newLine
- << newLine
- << "#ifndef " << headerGuard << newLine
- << "#define " << headerGuard << newLine
- << newLine
- << "#define JucePlugin_Build_VST " << ((bool) project.shouldBuildVST().getValue() ? 1 : 0) << " // (If you change this value, you'll also need to re-export the projects using the Jucer)" << newLine
- << "#define JucePlugin_Build_AU " << ((bool) project.shouldBuildAU().getValue() ? 1 : 0) << " // (If you change this value, you'll also need to re-export the projects using the Jucer)" << newLine
- << "#define JucePlugin_Build_RTAS " << ((bool) project.shouldBuildRTAS().getValue() ? 1 : 0) << " // (If you change this value, you'll also need to re-export the projects using the Jucer)" << newLine
- << newLine
- << "#define JucePlugin_Name " << project.getPluginName().toString().quoted() << newLine
- << "#define JucePlugin_Desc " << project.getPluginDesc().toString().quoted() << newLine
- << "#define JucePlugin_Manufacturer " << project.getPluginManufacturer().toString().quoted() << newLine
- << "#define JucePlugin_ManufacturerCode '" << project.getPluginManufacturerCode().toString().trim().substring (0, 4) << "'" << newLine
- << "#define JucePlugin_PluginCode '" << project.getPluginCode().toString().trim().substring (0, 4) << "'" << newLine
- << "#define JucePlugin_MaxNumInputChannels " << countMaxPluginChannels (project.getPluginChannelConfigs().toString(), true) << newLine
- << "#define JucePlugin_MaxNumOutputChannels " << countMaxPluginChannels (project.getPluginChannelConfigs().toString(), false) << newLine
- << "#define JucePlugin_PreferredChannelConfigurations " << project.getPluginChannelConfigs().toString() << newLine
- << "#define JucePlugin_IsSynth " << ((bool) project.getPluginIsSynth().getValue() ? 1 : 0) << newLine
- << "#define JucePlugin_WantsMidiInput " << ((bool) project.getPluginWantsMidiInput().getValue() ? 1 : 0) << newLine
- << "#define JucePlugin_ProducesMidiOutput " << ((bool) project.getPluginProducesMidiOut().getValue() ? 1 : 0) << newLine
- << "#define JucePlugin_SilenceInProducesSilenceOut " << ((bool) project.getPluginSilenceInProducesSilenceOut().getValue() ? 1 : 0) << newLine
- << "#define JucePlugin_TailLengthSeconds " << (double) project.getPluginTailLengthSeconds().getValue() << newLine
- << "#define JucePlugin_EditorRequiresKeyboardFocus " << ((bool) project.getPluginEditorNeedsKeyFocus().getValue() ? 1 : 0) << newLine
- << "#define JucePlugin_VersionCode " << project.getVersionAsHex() << newLine
- << "#define JucePlugin_VersionString " << project.getVersion().toString().quoted() << newLine
- << "#define JucePlugin_VSTUniqueID JucePlugin_PluginCode" << newLine
- << "#define JucePlugin_VSTCategory " << ((bool) project.getPluginIsSynth().getValue() ? "kPlugCategSynth" : "kPlugCategEffect") << newLine
- << "#define JucePlugin_AUMainType " << ((bool) project.getPluginIsSynth().getValue() ? "kAudioUnitType_MusicDevice" : "kAudioUnitType_Effect") << newLine
- << "#define JucePlugin_AUSubType JucePlugin_PluginCode" << newLine
- << "#define JucePlugin_AUExportPrefix " << project.getPluginAUExportPrefix().toString() << newLine
- << "#define JucePlugin_AUExportPrefixQuoted " << project.getPluginAUExportPrefix().toString().quoted() << newLine
- << "#define JucePlugin_AUManufacturerCode JucePlugin_ManufacturerCode" << newLine
- << "#define JucePlugin_CFBundleIdentifier " << project.getBundleIdentifier().toString() << newLine
- << "#define JucePlugin_AUCocoaViewClassName " << project.getPluginAUCocoaViewClassName().toString() << newLine
- << "#define JucePlugin_RTASCategory " << ((bool) project.getPluginIsSynth().getValue() ? "ePlugInCategory_SWGenerators" : "ePlugInCategory_None") << newLine
- << "#define JucePlugin_RTASManufacturerCode JucePlugin_ManufacturerCode" << newLine
- << "#define JucePlugin_RTASProductId JucePlugin_PluginCode" << newLine;
-
- out << "#define JUCE_USE_VSTSDK_2_4 1" << newLine
- << newLine
- << "#endif // " << headerGuard << newLine;
- }
-
- bool replaceFileIfDifferent (const File& f, const MemoryOutputStream& newData)
- {
- if (! FileHelpers::overwriteFileWithNewDataIfDifferent (f, newData))
- {
- errors.add ("Can't write to file: " + f.getFullPathName());
- return false;
- }
-
- return true;
- }
-
- void writeAppConfigFile()
- {
- appConfigFile = project.getGeneratedCodeFolder().getChildFile (project.getAppConfigFilename());
-
- MemoryOutputStream mem;
- if (writeAppConfig (mem))
- replaceFileIfDifferent (appConfigFile, mem);
- else
- appConfigFile.deleteFile();
- }
-
- void writeJuceSourceWrappers()
- {
- juceHeaderFile = project.getAppIncludeFile();
- binaryDataCpp = generatedCodeFolder.getChildFile ("BinaryData.cpp");
-
- if (resourceFile.getNumFiles() > 0)
- {
- //resourceFile.setJuceHeaderToInclude (juceHeaderFile);
- resourceFile.setClassName ("BinaryData");
-
- if (! resourceFile.write (binaryDataCpp))
- errors.add ("Can't create binary resources file: " + binaryDataCpp.getFullPathName());
- }
- else
- {
- binaryDataCpp.deleteFile();
- binaryDataCpp.withFileExtension ("h").deleteFile();
- }
-
- if (project.getProjectType().isLibrary())
- return;
-
- if (project.getProjectType().isAudioPlugin())
- {
- MemoryOutputStream mem;
- pluginCharacteristicsFile = generatedCodeFolder.getChildFile (project.getPluginCharacteristicsFilename());
- writePluginCharacteristics (pluginCharacteristicsFile, project, mem);
- replaceFileIfDifferent (pluginCharacteristicsFile, mem);
- }
-
- for (int i = 0; i <= project.getNumSeparateAmalgamatedFiles(); ++i)
- {
- const File sourceWrapperCpp (getSourceWrapperCpp (i));
- const File sourceWrapperMM (sourceWrapperCpp.withFileExtension (".mm"));
-
- if (numJuceSourceFiles > 0
- && ((i == 0 && numJuceSourceFiles == 1) || (i != 0 && numJuceSourceFiles > 1)))
- {
- MemoryOutputStream mem;
- writeSourceWrapper (mem, i);
- replaceFileIfDifferent (sourceWrapperCpp, mem);
- replaceFileIfDifferent (sourceWrapperMM, mem);
- }
- else
- {
- sourceWrapperMM.deleteFile();
- sourceWrapperCpp.deleteFile();
- }
- }
-
- if (hasAppHeaderFile)
- {
- MemoryOutputStream mem;
- writeAppHeader (mem);
- replaceFileIfDifferent (juceHeaderFile, mem);
- }
- else
- {
- juceHeaderFile.deleteFile();
- }
- }
-
- Project::Item createLibraryFilesGroup (ProjectExporter& exporter)
- {
- Project::Item libraryFiles (Project::Item::createGroup (project, project.getJuceCodeGroupName()));
-
- if (appConfigFile.exists())
- libraryFiles.addFile (appConfigFile, -1);
-
- if (hasAppHeaderFile)
- libraryFiles.addFile (juceHeaderFile, -1);
-
- if (hasResources)
- {
- libraryFiles.addFile (binaryDataCpp, -1);
- libraryFiles.addFile (binaryDataCpp.withFileExtension (".h"), -1);
- }
-
- if (numJuceSourceFiles > 0)
- {
- for (int j = 0; j <= project.getNumSeparateAmalgamatedFiles(); ++j)
- {
- const File sourceWrapperCpp (getSourceWrapperCpp (j));
- const File sourceWrapperMM (sourceWrapperCpp.withFileExtension (".mm"));
-
- if ((j == 0 && numJuceSourceFiles == 1) || (j != 0 && numJuceSourceFiles > 1))
- {
- if (exporter.usesMMFiles())
- libraryFiles.addFile (sourceWrapperMM, -1);
- else
- libraryFiles.addFile (sourceWrapperCpp, -1);
- }
- }
- }
-
- if (project.getProjectType().isAudioPlugin())
- libraryFiles.addFile (pluginCharacteristicsFile, -1);
-
- libraryFiles.setID ("__jucelibfiles");
- return libraryFiles;
- }
-
- void writeProjects()
- {
- for (int i = project.getNumExporters(); --i >= 0;)
- {
- ScopedPointer <ProjectExporter> exporter (project.createExporter (i));
- std::cout << "Writing files for: " << exporter->getName() << std::endl;
-
- const File targetFolder (exporter->getTargetFolder());
-
- if (targetFolder.createDirectory())
- {
- exporter->generatedGroups.add (createLibraryFilesGroup (*exporter));
-
- for (int j = 0; j < exporter->libraryModules.size(); ++j)
- exporter->libraryModules.getUnchecked(j)->addExtraCodeGroups (*exporter, exporter->generatedGroups);
-
- try
- {
- exporter->create();
- }
- catch (ProjectExporter::SaveError& error)
- {
- errors.add (error.message);
- }
- }
- else
- {
- errors.add ("Can't create folder: " + exporter->getTargetFolder().getFullPathName());
- }
- }
- }
-
- File getSourceWrapperCpp (int fileIndex) const
- {
- return project.getGeneratedCodeFolder()
- .getChildFile (project.getJuceSourceFilenameRoot() + (fileIndex != 0 ? String (fileIndex) : String::empty))
- .withFileExtension (".cpp");
- }
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectSaver);
- };
-
-
- #endif
|