- clean working copy for leaving SVN
This commit is contained in:
+695
@@ -0,0 +1,695 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class AndroidProjectExporter : public ProjectExporter
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
static const char* getNameAndroid() { return "Android Project"; }
|
||||
static const char* getValueTreeTypeName() { return "ANDROID"; }
|
||||
|
||||
static AndroidProjectExporter* createForSettings (Project& project, const ValueTree& settings)
|
||||
{
|
||||
if (settings.hasType (getValueTreeTypeName()))
|
||||
return new AndroidProjectExporter (project, settings);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
AndroidProjectExporter (Project& p, const ValueTree& t) : ProjectExporter (p, t)
|
||||
{
|
||||
name = getNameAndroid();
|
||||
|
||||
if (getTargetLocationString().isEmpty())
|
||||
getTargetLocationValue() = getDefaultBuildsRootFolder() + "Android";
|
||||
|
||||
if (getActivityClassPath().isEmpty())
|
||||
getActivityClassPathValue() = createDefaultClassName();
|
||||
|
||||
if (getSDKPathString().isEmpty()) getSDKPathValue() = "${user.home}/SDKs/android-sdk";
|
||||
if (getNDKPathString().isEmpty()) getNDKPathValue() = "${user.home}/SDKs/android-ndk";
|
||||
|
||||
if (getMinimumSDKVersionString().isEmpty())
|
||||
getMinimumSDKVersionValue() = 8;
|
||||
|
||||
if (getInternetNeededValue().toString().isEmpty())
|
||||
getInternetNeededValue() = true;
|
||||
|
||||
if (getKeyStoreValue().getValue().isVoid()) getKeyStoreValue() = "${user.home}/.android/debug.keystore";
|
||||
if (getKeyStorePassValue().getValue().isVoid()) getKeyStorePassValue() = "android";
|
||||
if (getKeyAliasValue().getValue().isVoid()) getKeyAliasValue() = "androiddebugkey";
|
||||
if (getKeyAliasPassValue().getValue().isVoid()) getKeyAliasPassValue() = "android";
|
||||
if (getCPP11EnabledValue().getValue().isVoid()) getCPP11EnabledValue() = true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool canLaunchProject() override { return false; }
|
||||
bool launchProject() override { return false; }
|
||||
bool isAndroid() const override { return true; }
|
||||
bool usesMMFiles() const override { return false; }
|
||||
bool canCopeWithDuplicateFiles() override { return false; }
|
||||
|
||||
void createExporterProperties (PropertyListBuilder& props) override
|
||||
{
|
||||
props.add (new TextPropertyComponent (getActivityClassPathValue(), "Android Activity class name", 256, false),
|
||||
"The full java class name to use for the app's Activity class.");
|
||||
|
||||
props.add (new TextPropertyComponent (getSDKPathValue(), "Android SDK Path", 1024, false),
|
||||
"The path to the Android SDK folder on the target build machine");
|
||||
|
||||
props.add (new TextPropertyComponent (getNDKPathValue(), "Android NDK Path", 1024, false),
|
||||
"The path to the Android NDK folder on the target build machine");
|
||||
|
||||
props.add (new TextPropertyComponent (getMinimumSDKVersionValue(), "Minimum SDK version", 32, false),
|
||||
"The number of the minimum version of the Android SDK that the app requires");
|
||||
|
||||
props.add (new TextPropertyComponent (getNDKToolchainVersionValue(), "NDK Toolchain version", 32, false),
|
||||
"The variable NDK_TOOLCHAIN_VERSION in Application.mk - leave blank for a default value");
|
||||
|
||||
props.add (new BooleanPropertyComponent (getCPP11EnabledValue(), "Enable C++11 features", "Enable the -std=c++11 flag"),
|
||||
"If enabled, this will set the -std=c++11 flag for the build.");
|
||||
|
||||
props.add (new BooleanPropertyComponent (getInternetNeededValue(), "Internet Access", "Specify internet access permission in the manifest"),
|
||||
"If enabled, this will set the android.permission.INTERNET flag in the manifest.");
|
||||
|
||||
props.add (new BooleanPropertyComponent (getAudioRecordNeededValue(), "Audio Input Required", "Specify audio record permission in the manifest"),
|
||||
"If enabled, this will set the android.permission.RECORD_AUDIO flag in the manifest.");
|
||||
|
||||
props.add (new TextPropertyComponent (getOtherPermissionsValue(), "Custom permissions", 2048, false),
|
||||
"A space-separated list of other permission flags that should be added to the manifest.");
|
||||
|
||||
props.add (new TextPropertyComponent (getKeyStoreValue(), "Key Signing: key.store", 2048, false),
|
||||
"The key.store value, used when signing the package.");
|
||||
props.add (new TextPropertyComponent (getKeyStorePassValue(), "Key Signing: key.store.password", 2048, false),
|
||||
"The key.store password, used when signing the package.");
|
||||
props.add (new TextPropertyComponent (getKeyAliasValue(), "Key Signing: key.alias", 2048, false),
|
||||
"The key.alias value, used when signing the package.");
|
||||
props.add (new TextPropertyComponent (getKeyAliasPassValue(), "Key Signing: key.alias.password", 2048, false),
|
||||
"The key.alias password, used when signing the package.");
|
||||
}
|
||||
|
||||
Value getActivityClassPathValue() { return getSetting (Ids::androidActivityClass); }
|
||||
String getActivityClassPath() const { return settings [Ids::androidActivityClass]; }
|
||||
Value getSDKPathValue() { return getSetting (Ids::androidSDKPath); }
|
||||
String getSDKPathString() const { return settings [Ids::androidSDKPath]; }
|
||||
Value getNDKPathValue() { return getSetting (Ids::androidNDKPath); }
|
||||
String getNDKPathString() const { return settings [Ids::androidNDKPath]; }
|
||||
Value getNDKToolchainVersionValue() { return getSetting (Ids::toolset); }
|
||||
String getNDKToolchainVersionString() const { return settings [Ids::toolset]; }
|
||||
|
||||
Value getKeyStoreValue() { return getSetting (Ids::androidKeyStore); }
|
||||
String getKeyStoreString() const { return settings [Ids::androidKeyStore]; }
|
||||
Value getKeyStorePassValue() { return getSetting (Ids::androidKeyStorePass); }
|
||||
String getKeyStorePassString() const { return settings [Ids::androidKeyStorePass]; }
|
||||
Value getKeyAliasValue() { return getSetting (Ids::androidKeyAlias); }
|
||||
String getKeyAliasString() const { return settings [Ids::androidKeyAlias]; }
|
||||
Value getKeyAliasPassValue() { return getSetting (Ids::androidKeyAliasPass); }
|
||||
String getKeyAliasPassString() const { return settings [Ids::androidKeyAliasPass]; }
|
||||
|
||||
Value getInternetNeededValue() { return getSetting (Ids::androidInternetNeeded); }
|
||||
bool getInternetNeeded() const { return settings [Ids::androidInternetNeeded]; }
|
||||
Value getAudioRecordNeededValue() { return getSetting (Ids::androidMicNeeded); }
|
||||
bool getAudioRecordNeeded() const { return settings [Ids::androidMicNeeded]; }
|
||||
Value getMinimumSDKVersionValue() { return getSetting (Ids::androidMinimumSDK); }
|
||||
String getMinimumSDKVersionString() const { return settings [Ids::androidMinimumSDK]; }
|
||||
Value getOtherPermissionsValue() { return getSetting (Ids::androidOtherPermissions); }
|
||||
String getOtherPermissions() const { return settings [Ids::androidOtherPermissions]; }
|
||||
|
||||
Value getCPP11EnabledValue() { return getSetting (Ids::androidCpp11); }
|
||||
bool isCPP11Enabled() const { return settings [Ids::androidCpp11]; }
|
||||
|
||||
String createDefaultClassName() const
|
||||
{
|
||||
String s (project.getBundleIdentifier().toString().toLowerCase());
|
||||
|
||||
if (s.length() > 5
|
||||
&& s.containsChar ('.')
|
||||
&& s.containsOnly ("abcdefghijklmnopqrstuvwxyz_.")
|
||||
&& ! s.startsWithChar ('.'))
|
||||
{
|
||||
if (! s.endsWithChar ('.'))
|
||||
s << ".";
|
||||
}
|
||||
else
|
||||
{
|
||||
s = "com.yourcompany.";
|
||||
}
|
||||
|
||||
return s + CodeHelpers::makeValidIdentifier (project.getProjectFilenameRoot(), false, true, false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void create (const OwnedArray<LibraryModule>& modules) const override
|
||||
{
|
||||
const File target (getTargetFolder());
|
||||
const File jniFolder (target.getChildFile ("jni"));
|
||||
|
||||
copyActivityJavaFiles (modules);
|
||||
createDirectoryOrThrow (jniFolder);
|
||||
createDirectoryOrThrow (target.getChildFile ("res").getChildFile ("values"));
|
||||
createDirectoryOrThrow (target.getChildFile ("libs"));
|
||||
createDirectoryOrThrow (target.getChildFile ("bin"));
|
||||
|
||||
{
|
||||
ScopedPointer<XmlElement> manifest (createManifestXML());
|
||||
writeXmlOrThrow (*manifest, target.getChildFile ("AndroidManifest.xml"), "utf-8", 100, true);
|
||||
}
|
||||
|
||||
writeApplicationMk (jniFolder.getChildFile ("Application.mk"));
|
||||
writeAndroidMk (jniFolder.getChildFile ("Android.mk"));
|
||||
|
||||
{
|
||||
ScopedPointer<XmlElement> antBuildXml (createAntBuildXML());
|
||||
writeXmlOrThrow (*antBuildXml, target.getChildFile ("build.xml"), "UTF-8", 100);
|
||||
}
|
||||
|
||||
writeProjectPropertiesFile (target.getChildFile ("project.properties"));
|
||||
writeLocalPropertiesFile (target.getChildFile ("local.properties"));
|
||||
writeStringsFile (target.getChildFile ("res/values/strings.xml"));
|
||||
|
||||
ScopedPointer<Drawable> bigIcon (getBigIcon());
|
||||
ScopedPointer<Drawable> smallIcon (getSmallIcon());
|
||||
|
||||
if (bigIcon != nullptr && smallIcon != nullptr)
|
||||
{
|
||||
const int step = jmax (bigIcon->getWidth(), bigIcon->getHeight()) / 8;
|
||||
writeIcon (target.getChildFile ("res/drawable-xhdpi/icon.png"), getBestIconForSize (step * 8, false));
|
||||
writeIcon (target.getChildFile ("res/drawable-hdpi/icon.png"), getBestIconForSize (step * 6, false));
|
||||
writeIcon (target.getChildFile ("res/drawable-mdpi/icon.png"), getBestIconForSize (step * 4, false));
|
||||
writeIcon (target.getChildFile ("res/drawable-ldpi/icon.png"), getBestIconForSize (step * 3, false));
|
||||
}
|
||||
else if (Drawable* icon = bigIcon != nullptr ? bigIcon : smallIcon)
|
||||
{
|
||||
writeIcon (target.getChildFile ("res/drawable-mdpi/icon.png"), rescaleImageForIcon (*icon, icon->getWidth()));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
class AndroidBuildConfiguration : public BuildConfiguration
|
||||
{
|
||||
public:
|
||||
AndroidBuildConfiguration (Project& p, const ValueTree& settings)
|
||||
: BuildConfiguration (p, settings)
|
||||
{
|
||||
if (getArchitectures().isEmpty())
|
||||
getArchitecturesValue() = "armeabi armeabi-v7a";
|
||||
}
|
||||
|
||||
Value getArchitecturesValue() { return getValue (Ids::androidArchitectures); }
|
||||
String getArchitectures() const { return config [Ids::androidArchitectures]; }
|
||||
|
||||
void createConfigProperties (PropertyListBuilder& props)
|
||||
{
|
||||
props.add (new TextPropertyComponent (getArchitecturesValue(), "Architectures", 256, false),
|
||||
"A list of the ARM architectures to build (for a fat binary).");
|
||||
}
|
||||
};
|
||||
|
||||
BuildConfiguration::Ptr createBuildConfig (const ValueTree& v) const
|
||||
{
|
||||
return new AndroidBuildConfiguration (project, v);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
XmlElement* createManifestXML() const
|
||||
{
|
||||
XmlElement* manifest = new XmlElement ("manifest");
|
||||
|
||||
manifest->setAttribute ("xmlns:android", "http://schemas.android.com/apk/res/android");
|
||||
manifest->setAttribute ("android:versionCode", "1");
|
||||
manifest->setAttribute ("android:versionName", "1.0");
|
||||
manifest->setAttribute ("package", getActivityClassPackage());
|
||||
|
||||
XmlElement* screens = manifest->createNewChildElement ("supports-screens");
|
||||
screens->setAttribute ("android:smallScreens", "true");
|
||||
screens->setAttribute ("android:normalScreens", "true");
|
||||
screens->setAttribute ("android:largeScreens", "true");
|
||||
//screens->setAttribute ("android:xlargeScreens", "true");
|
||||
screens->setAttribute ("android:anyDensity", "true");
|
||||
|
||||
XmlElement* sdk = manifest->createNewChildElement ("uses-sdk");
|
||||
sdk->setAttribute ("android:minSdkVersion", getMinimumSDKVersionString());
|
||||
sdk->setAttribute ("android:targetSdkVersion", "11");
|
||||
|
||||
{
|
||||
const StringArray permissions (getPermissionsRequired());
|
||||
|
||||
for (int i = permissions.size(); --i >= 0;)
|
||||
manifest->createNewChildElement ("uses-permission")->setAttribute ("android:name", permissions[i]);
|
||||
}
|
||||
|
||||
if (project.getModules().isModuleEnabled ("juce_opengl"))
|
||||
{
|
||||
XmlElement* feature = manifest->createNewChildElement ("uses-feature");
|
||||
feature->setAttribute ("android:glEsVersion", "0x00020000");
|
||||
feature->setAttribute ("android:required", "true");
|
||||
}
|
||||
|
||||
XmlElement* app = manifest->createNewChildElement ("application");
|
||||
app->setAttribute ("android:label", "@string/app_name");
|
||||
|
||||
{
|
||||
ScopedPointer<Drawable> bigIcon (getBigIcon()), smallIcon (getSmallIcon());
|
||||
|
||||
if (bigIcon != nullptr || smallIcon != nullptr)
|
||||
app->setAttribute ("android:icon", "@drawable/icon");
|
||||
}
|
||||
|
||||
if (getMinimumSDKVersionString().getIntValue() >= 11)
|
||||
app->setAttribute ("android:hardwareAccelerated", "false"); // (using the 2D acceleration slows down openGL)
|
||||
|
||||
XmlElement* act = app->createNewChildElement ("activity");
|
||||
act->setAttribute ("android:name", getActivityName());
|
||||
act->setAttribute ("android:label", "@string/app_name");
|
||||
act->setAttribute ("android:configChanges", "keyboardHidden|orientation");
|
||||
|
||||
XmlElement* intent = act->createNewChildElement ("intent-filter");
|
||||
intent->createNewChildElement ("action")->setAttribute ("android:name", "android.intent.action.MAIN");
|
||||
intent->createNewChildElement ("category")->setAttribute ("android:name", "android.intent.category.LAUNCHER");
|
||||
|
||||
return manifest;
|
||||
}
|
||||
|
||||
StringArray getPermissionsRequired() const
|
||||
{
|
||||
StringArray s;
|
||||
s.addTokens (getOtherPermissions(), ", ", "");
|
||||
|
||||
if (getInternetNeeded()) s.add ("android.permission.INTERNET");
|
||||
if (getAudioRecordNeeded()) s.add ("android.permission.RECORD_AUDIO");
|
||||
|
||||
s.trim();
|
||||
s.removeDuplicates (false);
|
||||
return s;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void findAllFilesToCompile (const Project::Item& projectItem, Array<RelativePath>& results) const
|
||||
{
|
||||
if (projectItem.isGroup())
|
||||
{
|
||||
for (int i = 0; i < projectItem.getNumChildren(); ++i)
|
||||
findAllFilesToCompile (projectItem.getChild(i), results);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (projectItem.shouldBeCompiled())
|
||||
results.add (RelativePath (projectItem.getFile(), getTargetFolder(), RelativePath::buildTargetFolder));
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String getActivityName() const
|
||||
{
|
||||
return getActivityClassPath().fromLastOccurrenceOf (".", false, false);
|
||||
}
|
||||
|
||||
String getActivityClassPackage() const
|
||||
{
|
||||
return getActivityClassPath().upToLastOccurrenceOf (".", false, false);
|
||||
}
|
||||
|
||||
String getJNIActivityClassName() const
|
||||
{
|
||||
return getActivityClassPath().replaceCharacter ('.', '/');
|
||||
}
|
||||
|
||||
static LibraryModule* getCoreModule (const OwnedArray<LibraryModule>& modules)
|
||||
{
|
||||
for (int i = modules.size(); --i >= 0;)
|
||||
if (modules.getUnchecked(i)->getID() == "juce_core")
|
||||
return modules.getUnchecked(i);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void copyActivityJavaFiles (const OwnedArray<LibraryModule>& modules) const
|
||||
{
|
||||
const String className (getActivityName());
|
||||
const String package (getActivityClassPackage());
|
||||
String path (package.replaceCharacter ('.', File::separator));
|
||||
|
||||
if (path.isEmpty() || className.isEmpty())
|
||||
throw SaveError ("Invalid Android Activity class name: " + getActivityClassPath());
|
||||
|
||||
const File classFolder (getTargetFolder().getChildFile ("src")
|
||||
.getChildFile (path));
|
||||
createDirectoryOrThrow (classFolder);
|
||||
|
||||
LibraryModule* const coreModule = getCoreModule (modules);
|
||||
|
||||
if (coreModule != nullptr)
|
||||
{
|
||||
File javaDestFile (classFolder.getChildFile (className + ".java"));
|
||||
|
||||
File javaSourceFile (coreModule->getFolder().getChildFile ("native")
|
||||
.getChildFile ("java")
|
||||
.getChildFile ("JuceAppActivity.java"));
|
||||
|
||||
MemoryOutputStream newFile;
|
||||
newFile << javaSourceFile.loadFileAsString()
|
||||
.replace ("JuceAppActivity", className)
|
||||
.replace ("package com.juce;", "package " + package + ";");
|
||||
|
||||
overwriteFileIfDifferentOrThrow (javaDestFile, newFile);
|
||||
}
|
||||
}
|
||||
|
||||
String getABIs (bool forDebug) const
|
||||
{
|
||||
for (ConstConfigIterator config (*this); config.next();)
|
||||
{
|
||||
const AndroidBuildConfiguration& androidConfig = dynamic_cast<const AndroidBuildConfiguration&> (*config);
|
||||
|
||||
if (config->isDebug() == forDebug)
|
||||
return androidConfig.getArchitectures();
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
String getCppFlags() const
|
||||
{
|
||||
String flags ("-fsigned-char -fexceptions -frtti");
|
||||
|
||||
if (! getNDKToolchainVersionString().startsWithIgnoreCase ("clang"))
|
||||
flags << " -Wno-psabi";
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
String getToolchainVersion() const
|
||||
{
|
||||
String v (getNDKToolchainVersionString());
|
||||
return v.isNotEmpty() ? v : "4.8";
|
||||
}
|
||||
|
||||
void writeApplicationMk (const File& file) const
|
||||
{
|
||||
MemoryOutputStream mo;
|
||||
|
||||
mo << "# Automatically generated makefile, created by the Introjucer" << newLine
|
||||
<< "# Don't edit this file! Your changes will be overwritten when you re-save the Introjucer project!" << newLine
|
||||
<< newLine
|
||||
<< "APP_STL := gnustl_static" << newLine
|
||||
<< "APP_CPPFLAGS += " << getCppFlags() << newLine
|
||||
<< "APP_PLATFORM := " << getAppPlatform() << newLine
|
||||
<< "NDK_TOOLCHAIN_VERSION := " << getToolchainVersion() << newLine
|
||||
<< newLine
|
||||
<< "ifeq ($(NDK_DEBUG),1)" << newLine
|
||||
<< " APP_ABI := " << getABIs (true) << newLine
|
||||
<< "else" << newLine
|
||||
<< " APP_ABI := " << getABIs (false) << newLine
|
||||
<< "endif" << newLine;
|
||||
|
||||
overwriteFileIfDifferentOrThrow (file, mo);
|
||||
}
|
||||
|
||||
void writeAndroidMk (const File& file) const
|
||||
{
|
||||
Array<RelativePath> files;
|
||||
|
||||
for (int i = 0; i < getAllGroups().size(); ++i)
|
||||
findAllFilesToCompile (getAllGroups().getReference(i), files);
|
||||
|
||||
MemoryOutputStream mo;
|
||||
writeAndroidMk (mo, files);
|
||||
|
||||
overwriteFileIfDifferentOrThrow (file, mo);
|
||||
}
|
||||
|
||||
void writeAndroidMk (OutputStream& out, const Array<RelativePath>& files) const
|
||||
{
|
||||
out << "# Automatically generated makefile, created by the Introjucer" << newLine
|
||||
<< "# Don't edit this file! Your changes will be overwritten when you re-save the Introjucer project!" << newLine
|
||||
<< newLine
|
||||
<< "LOCAL_PATH := $(call my-dir)" << newLine
|
||||
<< newLine
|
||||
<< "include $(CLEAR_VARS)" << newLine
|
||||
<< newLine
|
||||
<< "ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)" << newLine
|
||||
<< " LOCAL_ARM_MODE := arm" << newLine
|
||||
<< "endif" << newLine
|
||||
<< newLine
|
||||
<< "LOCAL_MODULE := juce_jni" << newLine
|
||||
<< "LOCAL_SRC_FILES := \\" << newLine;
|
||||
|
||||
for (int i = 0; i < files.size(); ++i)
|
||||
out << " " << (files.getReference(i).isAbsolute() ? "" : "../")
|
||||
<< escapeSpaces (files.getReference(i).toUnixStyle()) << "\\" << newLine;
|
||||
|
||||
String debugSettings, releaseSettings;
|
||||
|
||||
out << newLine
|
||||
<< "ifeq ($(NDK_DEBUG),1)" << newLine;
|
||||
writeConfigSettings (out, true);
|
||||
out << "else" << newLine;
|
||||
writeConfigSettings (out, false);
|
||||
out << "endif" << newLine
|
||||
<< newLine
|
||||
<< "include $(BUILD_SHARED_LIBRARY)" << newLine;
|
||||
}
|
||||
|
||||
void writeConfigSettings (OutputStream& out, bool forDebug) const
|
||||
{
|
||||
for (ConstConfigIterator config (*this); config.next();)
|
||||
{
|
||||
if (config->isDebug() == forDebug)
|
||||
{
|
||||
const AndroidBuildConfiguration& androidConfig = dynamic_cast<const AndroidBuildConfiguration&> (*config);
|
||||
|
||||
String cppFlags;
|
||||
cppFlags << createCPPFlags (androidConfig)
|
||||
<< (" " + replacePreprocessorTokens (androidConfig, getExtraCompilerFlagsString()).trim()).trimEnd()
|
||||
<< newLine
|
||||
<< getLDLIBS (androidConfig).trimEnd()
|
||||
<< newLine;
|
||||
|
||||
out << " LOCAL_CPPFLAGS += " << cppFlags;
|
||||
out << " LOCAL_CFLAGS += " << cppFlags;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String getLDLIBS (const AndroidBuildConfiguration& config) const
|
||||
{
|
||||
return " LOCAL_LDLIBS :=" + config.getGCCLibraryPathFlags()
|
||||
+ " -llog -lGLESv2 " + getExternalLibraryFlags (config)
|
||||
+ " " + replacePreprocessorTokens (config, getExtraLinkerFlagsString());
|
||||
}
|
||||
|
||||
String createIncludePathFlags (const BuildConfiguration& config) const
|
||||
{
|
||||
String flags;
|
||||
StringArray searchPaths (extraSearchPaths);
|
||||
searchPaths.addArray (config.getHeaderSearchPaths());
|
||||
searchPaths.removeDuplicates (false);
|
||||
|
||||
for (int i = 0; i < searchPaths.size(); ++i)
|
||||
flags << " -I " << FileHelpers::unixStylePath (replacePreprocessorTokens (config, searchPaths[i])).quoted();
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
String createCPPFlags (const BuildConfiguration& config) const
|
||||
{
|
||||
StringPairArray defines;
|
||||
defines.set ("JUCE_ANDROID", "1");
|
||||
defines.set ("JUCE_ANDROID_API_VERSION", getMinimumSDKVersionString());
|
||||
defines.set ("JUCE_ANDROID_ACTIVITY_CLASSNAME", getJNIActivityClassName().replaceCharacter ('/', '_'));
|
||||
defines.set ("JUCE_ANDROID_ACTIVITY_CLASSPATH", "\\\"" + getJNIActivityClassName() + "\\\"");
|
||||
|
||||
String flags ("-fsigned-char -fexceptions -frtti");
|
||||
|
||||
if (config.isDebug())
|
||||
{
|
||||
flags << " -g";
|
||||
defines.set ("DEBUG", "1");
|
||||
defines.set ("_DEBUG", "1");
|
||||
}
|
||||
else
|
||||
{
|
||||
defines.set ("NDEBUG", "1");
|
||||
}
|
||||
|
||||
flags << createIncludePathFlags (config)
|
||||
<< " -O" << config.getGCCOptimisationFlag();
|
||||
|
||||
if (isCPP11Enabled())
|
||||
flags << " -std=c++11 -std=gnu++11"; // these flags seem to enable slightly different things on gcc, and both seem to be needed
|
||||
|
||||
defines = mergePreprocessorDefs (defines, getAllPreprocessorDefs (config));
|
||||
return flags + createGCCPreprocessorFlags (defines);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
XmlElement* createAntBuildXML() const
|
||||
{
|
||||
XmlElement* proj = new XmlElement ("project");
|
||||
proj->setAttribute ("name", projectName);
|
||||
proj->setAttribute ("default", "debug");
|
||||
|
||||
proj->createNewChildElement ("loadproperties")->setAttribute ("srcFile", "local.properties");
|
||||
proj->createNewChildElement ("loadproperties")->setAttribute ("srcFile", "project.properties");
|
||||
|
||||
{
|
||||
XmlElement* target = proj->createNewChildElement ("target");
|
||||
target->setAttribute ("name", "clean");
|
||||
target->setAttribute ("depends", "android_rules.clean");
|
||||
|
||||
target->createNewChildElement ("delete")->setAttribute ("dir", "libs");
|
||||
target->createNewChildElement ("delete")->setAttribute ("dir", "obj");
|
||||
|
||||
XmlElement* executable = target->createNewChildElement ("exec");
|
||||
executable->setAttribute ("executable", "${ndk.dir}/ndk-build");
|
||||
executable->setAttribute ("dir", "${basedir}");
|
||||
executable->setAttribute ("failonerror", "true");
|
||||
|
||||
executable->createNewChildElement ("arg")->setAttribute ("value", "clean");
|
||||
}
|
||||
|
||||
{
|
||||
XmlElement* target = proj->createNewChildElement ("target");
|
||||
target->setAttribute ("name", "-pre-build");
|
||||
|
||||
addDebugConditionClause (target, "makefileConfig", "Debug", "Release");
|
||||
addDebugConditionClause (target, "ndkDebugValue", "NDK_DEBUG=1", "NDK_DEBUG=0");
|
||||
|
||||
String debugABIs, releaseABIs;
|
||||
|
||||
for (ConstConfigIterator config (*this); config.next();)
|
||||
{
|
||||
const AndroidBuildConfiguration& androidConfig = dynamic_cast<const AndroidBuildConfiguration&> (*config);
|
||||
|
||||
if (config->isDebug())
|
||||
debugABIs = androidConfig.getArchitectures();
|
||||
else
|
||||
releaseABIs = androidConfig.getArchitectures();
|
||||
}
|
||||
|
||||
addDebugConditionClause (target, "app_abis", debugABIs, releaseABIs);
|
||||
|
||||
XmlElement* executable = target->createNewChildElement ("exec");
|
||||
executable->setAttribute ("executable", "${ndk.dir}/ndk-build");
|
||||
executable->setAttribute ("dir", "${basedir}");
|
||||
executable->setAttribute ("failonerror", "true");
|
||||
|
||||
executable->createNewChildElement ("arg")->setAttribute ("value", "--jobs=2");
|
||||
executable->createNewChildElement ("arg")->setAttribute ("value", "CONFIG=${makefileConfig}");
|
||||
executable->createNewChildElement ("arg")->setAttribute ("value", "${ndkDebugValue}");
|
||||
executable->createNewChildElement ("arg")->setAttribute ("value", "APP_ABI=${app_abis}");
|
||||
|
||||
target->createNewChildElement ("delete")->setAttribute ("file", "${out.final.file}");
|
||||
target->createNewChildElement ("delete")->setAttribute ("file", "${out.packaged.file}");
|
||||
}
|
||||
|
||||
proj->createNewChildElement ("import")->setAttribute ("file", "${sdk.dir}/tools/ant/build.xml");
|
||||
|
||||
return proj;
|
||||
}
|
||||
|
||||
void addDebugConditionClause (XmlElement* target, const String& property,
|
||||
const String& debugValue, const String& releaseValue) const
|
||||
{
|
||||
XmlElement* condition = target->createNewChildElement ("condition");
|
||||
condition->setAttribute ("property", property);
|
||||
condition->setAttribute ("value", debugValue);
|
||||
condition->setAttribute ("else", releaseValue);
|
||||
|
||||
XmlElement* equals = condition->createNewChildElement ("equals");
|
||||
equals->setAttribute ("arg1", "${ant.project.invoked-targets}");
|
||||
equals->setAttribute ("arg2", "debug");
|
||||
}
|
||||
|
||||
String getAppPlatform() const
|
||||
{
|
||||
int ndkVersion = getMinimumSDKVersionString().getIntValue();
|
||||
if (ndkVersion == 9)
|
||||
ndkVersion = 10; // (doesn't seem to be a version '9')
|
||||
|
||||
return "android-" + String (ndkVersion);
|
||||
}
|
||||
|
||||
void writeProjectPropertiesFile (const File& file) const
|
||||
{
|
||||
MemoryOutputStream mo;
|
||||
mo << "# This file is used to override default values used by the Ant build system." << newLine
|
||||
<< "# It is automatically generated - DO NOT EDIT IT or your changes will be lost!." << newLine
|
||||
<< newLine
|
||||
<< "target=" << getAppPlatform() << newLine
|
||||
<< newLine;
|
||||
|
||||
overwriteFileIfDifferentOrThrow (file, mo);
|
||||
}
|
||||
|
||||
void writeLocalPropertiesFile (const File& file) const
|
||||
{
|
||||
MemoryOutputStream mo;
|
||||
mo << "# This file is used to override default values used by the Ant build system." << newLine
|
||||
<< "# It is automatically generated by the Introjucer - DO NOT EDIT IT or your changes will be lost!." << newLine
|
||||
<< newLine
|
||||
<< "sdk.dir=" << escapeSpaces (replacePreprocessorDefs (getAllPreprocessorDefs(), getSDKPathString())) << newLine
|
||||
<< "ndk.dir=" << escapeSpaces (replacePreprocessorDefs (getAllPreprocessorDefs(), getNDKPathString())) << newLine
|
||||
<< "key.store=" << getKeyStoreString() << newLine
|
||||
<< "key.alias=" << getKeyAliasString() << newLine
|
||||
<< "key.store.password=" << getKeyStorePassString() << newLine
|
||||
<< "key.alias.password=" << getKeyAliasPassString() << newLine
|
||||
<< newLine;
|
||||
|
||||
overwriteFileIfDifferentOrThrow (file, mo);
|
||||
}
|
||||
|
||||
void writeIcon (const File& file, const Image& im) const
|
||||
{
|
||||
if (im.isValid())
|
||||
{
|
||||
createDirectoryOrThrow (file.getParentDirectory());
|
||||
|
||||
PNGImageFormat png;
|
||||
MemoryOutputStream mo;
|
||||
|
||||
if (! png.writeImageToStream (im, mo))
|
||||
throw SaveError ("Can't generate Android icon file");
|
||||
|
||||
overwriteFileIfDifferentOrThrow (file, mo);
|
||||
}
|
||||
}
|
||||
|
||||
void writeStringsFile (const File& file) const
|
||||
{
|
||||
XmlElement strings ("resources");
|
||||
XmlElement* resourceName = strings.createNewChildElement ("string");
|
||||
resourceName->setAttribute ("name", "app_name");
|
||||
resourceName->addTextElement (projectName);
|
||||
|
||||
writeXmlOrThrow (strings, file, "utf-8", 100);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE (AndroidProjectExporter)
|
||||
};
|
||||
+344
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class CodeBlocksProjectExporter : public ProjectExporter
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
static const char* getNameCodeBlocks() { return "Code::Blocks project"; }
|
||||
static const char* getValueTreeTypeName() { return "CODEBLOCKS"; }
|
||||
|
||||
static CodeBlocksProjectExporter* createForSettings (Project& project, const ValueTree& settings)
|
||||
{
|
||||
if (settings.hasType (getValueTreeTypeName()))
|
||||
return new CodeBlocksProjectExporter (project, settings);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
CodeBlocksProjectExporter (Project& p, const ValueTree& t) : ProjectExporter (p, t)
|
||||
{
|
||||
name = getNameCodeBlocks();
|
||||
|
||||
if (getTargetLocationString().isEmpty())
|
||||
getTargetLocationValue() = getDefaultBuildsRootFolder() + "CodeBlocks";
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool canLaunchProject() override { return false; }
|
||||
bool launchProject() override { return false; }
|
||||
bool isCodeBlocks() const override { return true; }
|
||||
bool isWindows() const override { return true; }
|
||||
bool usesMMFiles() const override { return false; }
|
||||
bool canCopeWithDuplicateFiles() override { return false; }
|
||||
|
||||
void createExporterProperties (PropertyListBuilder&) override
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void create (const OwnedArray<LibraryModule>&) const override
|
||||
{
|
||||
const File cbpFile (getTargetFolder().getChildFile (project.getProjectFilenameRoot())
|
||||
.withFileExtension (".cbp"));
|
||||
|
||||
XmlElement xml ("CodeBlocks_project_file");
|
||||
addVersion (xml);
|
||||
createProject (*xml.createNewChildElement ("Project"));
|
||||
writeXmlOrThrow (xml, cbpFile, "UTF-8", 10);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class CodeBlocksBuildConfiguration : public BuildConfiguration
|
||||
{
|
||||
public:
|
||||
CodeBlocksBuildConfiguration (Project& p, const ValueTree& settings)
|
||||
: BuildConfiguration (p, settings)
|
||||
{
|
||||
}
|
||||
|
||||
void createConfigProperties (PropertyListBuilder&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
BuildConfiguration::Ptr createBuildConfig (const ValueTree& tree) const override
|
||||
{
|
||||
return new CodeBlocksBuildConfiguration (project, tree);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void addVersion (XmlElement& xml) const
|
||||
{
|
||||
XmlElement* fileVersion = xml.createNewChildElement ("FileVersion");
|
||||
fileVersion->setAttribute ("major", 1);
|
||||
fileVersion->setAttribute ("minor", 6);
|
||||
}
|
||||
|
||||
void addOptions (XmlElement& xml) const
|
||||
{
|
||||
xml.createNewChildElement ("Option")->setAttribute ("title", project.getTitle());
|
||||
xml.createNewChildElement ("Option")->setAttribute ("pch_mode", 2);
|
||||
xml.createNewChildElement ("Option")->setAttribute ("compiler", "gcc");
|
||||
}
|
||||
|
||||
static StringArray cleanArray (StringArray s)
|
||||
{
|
||||
s.trim();
|
||||
s.removeDuplicates (false);
|
||||
s.removeEmptyStrings (true);
|
||||
return s;
|
||||
}
|
||||
|
||||
StringArray getDefines (const BuildConfiguration& config) const
|
||||
{
|
||||
StringPairArray defines;
|
||||
defines.set ("__MINGW__", "1");
|
||||
defines.set ("__MINGW_EXTENSION", String::empty);
|
||||
|
||||
if (config.isDebug())
|
||||
{
|
||||
defines.set ("DEBUG", "1");
|
||||
defines.set ("_DEBUG", "1");
|
||||
}
|
||||
else
|
||||
{
|
||||
defines.set ("NDEBUG", "1");
|
||||
}
|
||||
|
||||
defines = mergePreprocessorDefs (defines, getAllPreprocessorDefs (config));
|
||||
|
||||
StringArray defs;
|
||||
for (int i = 0; i < defines.size(); ++i)
|
||||
defs.add (defines.getAllKeys()[i] + "=" + defines.getAllValues()[i]);
|
||||
|
||||
return cleanArray (defs);
|
||||
}
|
||||
|
||||
StringArray getCompilerFlags (const BuildConfiguration& config) const
|
||||
{
|
||||
StringArray flags;
|
||||
flags.add ("-O" + config.getGCCOptimisationFlag());
|
||||
flags.add ("-std=c++11");
|
||||
flags.add ("-mstackrealign");
|
||||
|
||||
if (config.isDebug())
|
||||
flags.add ("-g");
|
||||
|
||||
flags.addTokens (replacePreprocessorTokens (config, getExtraCompilerFlagsString()).trim(),
|
||||
" \n", "\"'");
|
||||
|
||||
{
|
||||
const StringArray defines (getDefines (config));
|
||||
|
||||
for (int i = 0; i < defines.size(); ++i)
|
||||
{
|
||||
String def (defines[i]);
|
||||
|
||||
if (! def.containsChar ('='))
|
||||
def << '=';
|
||||
|
||||
flags.add ("-D" + def);
|
||||
}
|
||||
}
|
||||
|
||||
return cleanArray (flags);
|
||||
}
|
||||
|
||||
StringArray getLinkerFlags (const BuildConfiguration& config) const
|
||||
{
|
||||
StringArray flags;
|
||||
|
||||
if (! config.isDebug())
|
||||
flags.add ("-s");
|
||||
|
||||
flags.addTokens (replacePreprocessorTokens (config, getExtraLinkerFlagsString()).trim(),
|
||||
" \n", "\"'");
|
||||
|
||||
return cleanArray (flags);
|
||||
}
|
||||
|
||||
StringArray getIncludePaths (const BuildConfiguration& config) const
|
||||
{
|
||||
StringArray paths;
|
||||
paths.add (".");
|
||||
paths.add (RelativePath (project.getGeneratedCodeFolder(),
|
||||
getTargetFolder(), RelativePath::buildTargetFolder).toWindowsStyle());
|
||||
|
||||
paths.addArray (config.getHeaderSearchPaths());
|
||||
return cleanArray (paths);
|
||||
}
|
||||
|
||||
static int getTypeIndex (const ProjectType& type)
|
||||
{
|
||||
if (type.isGUIApplication()) return 0;
|
||||
if (type.isCommandLineApp()) return 1;
|
||||
if (type.isStaticLibrary()) return 2;
|
||||
if (type.isDynamicLibrary()) return 3;
|
||||
if (type.isAudioPlugin()) return 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void createBuildTarget (XmlElement& xml, const BuildConfiguration& config) const
|
||||
{
|
||||
xml.setAttribute ("title", config.getName());
|
||||
|
||||
{
|
||||
XmlElement* output = xml.createNewChildElement ("Option");
|
||||
|
||||
String outputPath;
|
||||
if (config.getTargetBinaryRelativePathString().isNotEmpty())
|
||||
{
|
||||
RelativePath binaryPath (config.getTargetBinaryRelativePathString(), RelativePath::projectFolder);
|
||||
binaryPath = binaryPath.rebased (projectFolder, getTargetFolder(), RelativePath::buildTargetFolder);
|
||||
outputPath = config.getTargetBinaryRelativePathString();
|
||||
}
|
||||
else
|
||||
{
|
||||
outputPath ="bin/" + File::createLegalFileName (config.getName().trim());
|
||||
}
|
||||
|
||||
output->setAttribute ("output", outputPath + "/" + replacePreprocessorTokens (config, config.getTargetBinaryNameString()));
|
||||
|
||||
output->setAttribute ("prefix_auto", 1);
|
||||
output->setAttribute ("extension_auto", 1);
|
||||
}
|
||||
|
||||
xml.createNewChildElement ("Option")
|
||||
->setAttribute ("object_output", "obj/" + File::createLegalFileName (config.getName().trim()));
|
||||
|
||||
xml.createNewChildElement ("Option")->setAttribute ("type", getTypeIndex (project.getProjectType()));
|
||||
xml.createNewChildElement ("Option")->setAttribute ("compiler", "gcc");
|
||||
|
||||
{
|
||||
XmlElement* const compiler = xml.createNewChildElement ("Compiler");
|
||||
|
||||
{
|
||||
const StringArray compilerFlags (getCompilerFlags (config));
|
||||
|
||||
for (int i = 0; i < compilerFlags.size(); ++i)
|
||||
setAddOption (*compiler, "option", compilerFlags[i]);
|
||||
}
|
||||
|
||||
{
|
||||
const StringArray includePaths (getIncludePaths (config));
|
||||
|
||||
for (int i = 0; i < includePaths.size(); ++i)
|
||||
setAddOption (*compiler, "directory", includePaths[i]);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
XmlElement* const linker = xml.createNewChildElement ("Linker");
|
||||
|
||||
const StringArray linkerFlags (getLinkerFlags (config));
|
||||
for (int i = 0; i < linkerFlags.size(); ++i)
|
||||
setAddOption (*linker, "option", linkerFlags[i]);
|
||||
|
||||
for (int i = 0; i < mingwLibs.size(); ++i)
|
||||
setAddOption (*linker, "library", mingwLibs[i]);
|
||||
|
||||
const StringArray librarySearchPaths (config.getLibrarySearchPaths());
|
||||
for (int i = 0; i < librarySearchPaths.size(); ++i)
|
||||
setAddOption (*linker, "directory", replacePreprocessorDefs (getAllPreprocessorDefs(), librarySearchPaths[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void addBuild (XmlElement& xml) const
|
||||
{
|
||||
XmlElement* const build = xml.createNewChildElement ("Build");
|
||||
|
||||
for (ConstConfigIterator config (*this); config.next();)
|
||||
createBuildTarget (*build->createNewChildElement ("Target"), *config);
|
||||
}
|
||||
|
||||
void addProjectCompilerOptions (XmlElement& xml) const
|
||||
{
|
||||
XmlElement* const compiler = xml.createNewChildElement ("Compiler");
|
||||
setAddOption (*compiler, "option", "-Wall");
|
||||
setAddOption (*compiler, "option", "-Wno-strict-aliasing");
|
||||
setAddOption (*compiler, "option", "-Wno-strict-overflow");
|
||||
}
|
||||
|
||||
void addProjectLinkerOptions (XmlElement& xml) const
|
||||
{
|
||||
XmlElement* const linker = xml.createNewChildElement ("Linker");
|
||||
|
||||
static const char* defaultLibs[] = { "gdi32", "user32", "kernel32", "comctl32" };
|
||||
|
||||
StringArray libs (defaultLibs, numElementsInArray (defaultLibs));
|
||||
libs.addTokens (getExternalLibrariesString(), ";\n", "\"'");
|
||||
|
||||
libs = cleanArray (libs);
|
||||
|
||||
for (int i = 0; i < libs.size(); ++i)
|
||||
setAddOption (*linker, "library", replacePreprocessorDefs (getAllPreprocessorDefs(), libs[i]));
|
||||
}
|
||||
|
||||
void addCompileUnits (const Project::Item& projectItem, XmlElement& xml) const
|
||||
{
|
||||
if (projectItem.isGroup())
|
||||
{
|
||||
for (int i = 0; i < projectItem.getNumChildren(); ++i)
|
||||
addCompileUnits (projectItem.getChild(i), xml);
|
||||
}
|
||||
else if (projectItem.shouldBeAddedToTargetProject())
|
||||
{
|
||||
const RelativePath file (projectItem.getFile(), getTargetFolder(), RelativePath::buildTargetFolder);
|
||||
|
||||
XmlElement* unit = xml.createNewChildElement ("Unit");
|
||||
unit->setAttribute ("filename", file.toUnixStyle());
|
||||
|
||||
if (! projectItem.shouldBeCompiled())
|
||||
{
|
||||
unit->createNewChildElement("Option")->setAttribute ("compile", 0);
|
||||
unit->createNewChildElement("Option")->setAttribute ("link", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addCompileUnits (XmlElement& xml) const
|
||||
{
|
||||
for (int i = 0; i < getAllGroups().size(); ++i)
|
||||
addCompileUnits (getAllGroups().getReference(i), xml);
|
||||
}
|
||||
|
||||
void createProject (XmlElement& xml) const
|
||||
{
|
||||
addOptions (xml);
|
||||
addBuild (xml);
|
||||
addProjectCompilerOptions (xml);
|
||||
addProjectLinkerOptions (xml);
|
||||
addCompileUnits (xml);
|
||||
}
|
||||
|
||||
void setAddOption (XmlElement& xml, const String& nm, const String& value) const
|
||||
{
|
||||
xml.createNewChildElement ("Add")->setAttribute (nm, value);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (CodeBlocksProjectExporter)
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class MakefileProjectExporter : public ProjectExporter
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
static const char* getNameLinux() { return "Linux Makefile"; }
|
||||
static const char* getValueTreeTypeName() { return "LINUX_MAKE"; }
|
||||
|
||||
static MakefileProjectExporter* createForSettings (Project& project, const ValueTree& settings)
|
||||
{
|
||||
if (settings.hasType (getValueTreeTypeName()))
|
||||
return new MakefileProjectExporter (project, settings);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
MakefileProjectExporter (Project& p, const ValueTree& t) : ProjectExporter (p, t)
|
||||
{
|
||||
name = getNameLinux();
|
||||
|
||||
if (getTargetLocationString().isEmpty())
|
||||
getTargetLocationValue() = getDefaultBuildsRootFolder() + "Linux";
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool canLaunchProject() override { return false; }
|
||||
bool launchProject() override { return false; }
|
||||
bool usesMMFiles() const override { return false; }
|
||||
bool isLinux() const override { return true; }
|
||||
bool canCopeWithDuplicateFiles() override { return false; }
|
||||
|
||||
void createExporterProperties (PropertyListBuilder&) override
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void create (const OwnedArray<LibraryModule>&) const override
|
||||
{
|
||||
Array<RelativePath> files;
|
||||
for (int i = 0; i < getAllGroups().size(); ++i)
|
||||
findAllFilesToCompile (getAllGroups().getReference(i), files);
|
||||
|
||||
MemoryOutputStream mo;
|
||||
writeMakefile (mo, files);
|
||||
|
||||
overwriteFileIfDifferentOrThrow (getTargetFolder().getChildFile ("Makefile"), mo);
|
||||
}
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
class MakeBuildConfiguration : public BuildConfiguration
|
||||
{
|
||||
public:
|
||||
MakeBuildConfiguration (Project& p, const ValueTree& settings)
|
||||
: BuildConfiguration (p, settings)
|
||||
{
|
||||
setValueIfVoid (getLibrarySearchPathValue(), "/usr/X11R6/lib/");
|
||||
}
|
||||
|
||||
Value getArchitectureType() { return getValue (Ids::linuxArchitecture); }
|
||||
String getArchitectureTypeString() const { return config [Ids::linuxArchitecture]; }
|
||||
|
||||
void createConfigProperties (PropertyListBuilder& props) override
|
||||
{
|
||||
static const char* const archNames[] = { "(Default)", "32-bit (-m32)", "64-bit (-m64)", "ARM v6", "ARM v7" };
|
||||
const var archFlags[] = { var(), "-m32", "-m64", "-march=armv6", "-march=armv7" };
|
||||
|
||||
props.add (new ChoicePropertyComponent (getArchitectureType(), "Architecture",
|
||||
StringArray (archNames, numElementsInArray (archNames)),
|
||||
Array<var> (archFlags, numElementsInArray (archFlags))));
|
||||
}
|
||||
};
|
||||
|
||||
BuildConfiguration::Ptr createBuildConfig (const ValueTree& tree) const override
|
||||
{
|
||||
return new MakeBuildConfiguration (project, tree);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void findAllFilesToCompile (const Project::Item& projectItem, Array<RelativePath>& results) const
|
||||
{
|
||||
if (projectItem.isGroup())
|
||||
{
|
||||
for (int i = 0; i < projectItem.getNumChildren(); ++i)
|
||||
findAllFilesToCompile (projectItem.getChild(i), results);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (projectItem.shouldBeCompiled())
|
||||
results.add (RelativePath (projectItem.getFile(), getTargetFolder(), RelativePath::buildTargetFolder));
|
||||
}
|
||||
}
|
||||
|
||||
void writeDefineFlags (OutputStream& out, const BuildConfiguration& config) const
|
||||
{
|
||||
StringPairArray defines;
|
||||
defines.set ("LINUX", "1");
|
||||
|
||||
if (config.isDebug())
|
||||
{
|
||||
defines.set ("DEBUG", "1");
|
||||
defines.set ("_DEBUG", "1");
|
||||
}
|
||||
else
|
||||
{
|
||||
defines.set ("NDEBUG", "1");
|
||||
}
|
||||
|
||||
out << createGCCPreprocessorFlags (mergePreprocessorDefs (defines, getAllPreprocessorDefs (config)));
|
||||
}
|
||||
|
||||
void writeHeaderPathFlags (OutputStream& out, const BuildConfiguration& config) const
|
||||
{
|
||||
StringArray searchPaths (extraSearchPaths);
|
||||
searchPaths.addArray (config.getHeaderSearchPaths());
|
||||
|
||||
searchPaths.insert (0, "/usr/include/freetype2");
|
||||
searchPaths.insert (0, "/usr/include");
|
||||
|
||||
searchPaths.removeDuplicates (false);
|
||||
|
||||
for (int i = 0; i < searchPaths.size(); ++i)
|
||||
out << " -I " << addQuotesIfContainsSpaces (FileHelpers::unixStylePath (replacePreprocessorTokens (config, searchPaths[i])));
|
||||
}
|
||||
|
||||
void writeCppFlags (OutputStream& out, const BuildConfiguration& config) const
|
||||
{
|
||||
out << " CPPFLAGS := $(DEPFLAGS) -std=c++11";
|
||||
writeDefineFlags (out, config);
|
||||
writeHeaderPathFlags (out, config);
|
||||
out << newLine;
|
||||
}
|
||||
|
||||
void writeLinkerFlags (OutputStream& out, const BuildConfiguration& config) const
|
||||
{
|
||||
out << " LDFLAGS += $(TARGET_ARCH) -L$(BINDIR) -L$(LIBDIR)";
|
||||
|
||||
if (makefileIsDLL)
|
||||
out << " -shared";
|
||||
|
||||
if (! config.isDebug())
|
||||
out << " -fvisibility=hidden";
|
||||
|
||||
out << config.getGCCLibraryPathFlags();
|
||||
|
||||
for (int i = 0; i < linuxLibs.size(); ++i)
|
||||
out << " -l" << linuxLibs[i];
|
||||
|
||||
StringArray libraries;
|
||||
libraries.addTokens (getExternalLibrariesString(), ";", "\"'");
|
||||
libraries.removeEmptyStrings();
|
||||
|
||||
if (libraries.size() != 0)
|
||||
out << " -l" << replacePreprocessorTokens (config, libraries.joinIntoString (" -l")).trim();
|
||||
|
||||
out << " " << replacePreprocessorTokens (config, getExtraLinkerFlagsString()).trim()
|
||||
<< newLine;
|
||||
}
|
||||
|
||||
void writeConfig (OutputStream& out, const BuildConfiguration& config) const
|
||||
{
|
||||
const String buildDirName ("build");
|
||||
const String intermediatesDirName (buildDirName + "/intermediate/" + config.getName());
|
||||
String outputDir (buildDirName);
|
||||
|
||||
if (config.getTargetBinaryRelativePathString().isNotEmpty())
|
||||
{
|
||||
RelativePath binaryPath (config.getTargetBinaryRelativePathString(), RelativePath::projectFolder);
|
||||
outputDir = binaryPath.rebased (projectFolder, getTargetFolder(), RelativePath::buildTargetFolder).toUnixStyle();
|
||||
}
|
||||
|
||||
out << "ifeq ($(CONFIG)," << escapeSpaces (config.getName()) << ")" << newLine;
|
||||
out << " BINDIR := " << escapeSpaces (buildDirName) << newLine
|
||||
<< " LIBDIR := " << escapeSpaces (buildDirName) << newLine
|
||||
<< " OBJDIR := " << escapeSpaces (intermediatesDirName) << newLine
|
||||
<< " OUTDIR := " << escapeSpaces (outputDir) << newLine
|
||||
<< newLine
|
||||
<< " ifeq ($(TARGET_ARCH),)" << newLine
|
||||
<< " TARGET_ARCH := " << getArchFlags (config) << newLine
|
||||
<< " endif" << newLine
|
||||
<< newLine;
|
||||
|
||||
writeCppFlags (out, config);
|
||||
|
||||
out << " CFLAGS += $(CPPFLAGS) $(TARGET_ARCH)";
|
||||
|
||||
if (config.isDebug())
|
||||
out << " -g -ggdb";
|
||||
|
||||
if (makefileIsDLL)
|
||||
out << " -fPIC";
|
||||
|
||||
out << " -O" << config.getGCCOptimisationFlag()
|
||||
<< (" " + replacePreprocessorTokens (config, getExtraCompilerFlagsString())).trimEnd()
|
||||
<< newLine;
|
||||
|
||||
out << " CXXFLAGS += $(CFLAGS)" << newLine;
|
||||
|
||||
writeLinkerFlags (out, config);
|
||||
|
||||
out << " LDDEPS :=" << newLine
|
||||
<< " RESFLAGS := ";
|
||||
writeDefineFlags (out, config);
|
||||
writeHeaderPathFlags (out, config);
|
||||
out << newLine;
|
||||
|
||||
String targetName (replacePreprocessorTokens (config, config.getTargetBinaryNameString()));
|
||||
|
||||
if (projectType.isStaticLibrary() || projectType.isDynamicLibrary())
|
||||
targetName = getLibbedFilename (targetName);
|
||||
else
|
||||
targetName = targetName.upToLastOccurrenceOf (".", false, false) + makefileTargetSuffix;
|
||||
|
||||
out << " TARGET := " << escapeSpaces (targetName) << newLine;
|
||||
|
||||
if (projectType.isStaticLibrary())
|
||||
out << " BLDCMD = ar -rcs $(OUTDIR)/$(TARGET) $(OBJECTS)" << newLine;
|
||||
else
|
||||
out << " BLDCMD = $(CXX) -o $(OUTDIR)/$(TARGET) $(OBJECTS) $(LDFLAGS) $(RESOURCES) $(TARGET_ARCH)" << newLine;
|
||||
|
||||
out << " CLEANCMD = rm -rf $(OUTDIR)/$(TARGET) $(OBJDIR)" << newLine
|
||||
<< "endif" << newLine
|
||||
<< newLine;
|
||||
}
|
||||
|
||||
void writeObjects (OutputStream& out, const Array<RelativePath>& files) const
|
||||
{
|
||||
out << "OBJECTS := \\" << newLine;
|
||||
|
||||
for (int i = 0; i < files.size(); ++i)
|
||||
if (shouldFileBeCompiledByDefault (files.getReference(i)))
|
||||
out << " $(OBJDIR)/" << escapeSpaces (getObjectFileFor (files.getReference(i))) << " \\" << newLine;
|
||||
|
||||
out << newLine;
|
||||
}
|
||||
|
||||
void writeMakefile (OutputStream& out, const Array<RelativePath>& files) const
|
||||
{
|
||||
out << "# Automatically generated makefile, created by the Introjucer" << newLine
|
||||
<< "# Don't edit this file! Your changes will be overwritten when you re-save the Introjucer project!" << newLine
|
||||
<< newLine;
|
||||
|
||||
out << "# (this disables dependency generation if multiple architectures are set)" << newLine
|
||||
<< "DEPFLAGS := $(if $(word 2, $(TARGET_ARCH)), , -MMD)" << newLine
|
||||
<< newLine;
|
||||
|
||||
out << "ifndef CONFIG" << newLine
|
||||
<< " CONFIG=" << escapeSpaces (getConfiguration(0)->getName()) << newLine
|
||||
<< "endif" << newLine
|
||||
<< newLine;
|
||||
|
||||
for (ConstConfigIterator config (*this); config.next();)
|
||||
writeConfig (out, *config);
|
||||
|
||||
writeObjects (out, files);
|
||||
|
||||
out << ".PHONY: clean" << newLine
|
||||
<< newLine;
|
||||
|
||||
out << "$(OUTDIR)/$(TARGET): $(OBJECTS) $(LDDEPS) $(RESOURCES)" << newLine
|
||||
<< "\t@echo Linking " << projectName << newLine
|
||||
<< "\t-@mkdir -p $(BINDIR)" << newLine
|
||||
<< "\t-@mkdir -p $(LIBDIR)" << newLine
|
||||
<< "\t-@mkdir -p $(OUTDIR)" << newLine
|
||||
<< "\t@$(BLDCMD)" << newLine
|
||||
<< newLine;
|
||||
|
||||
out << "clean:" << newLine
|
||||
<< "\t@echo Cleaning " << projectName << newLine
|
||||
<< "\t@$(CLEANCMD)" << newLine
|
||||
<< newLine;
|
||||
|
||||
out << "strip:" << newLine
|
||||
<< "\t@echo Stripping " << projectName << newLine
|
||||
<< "\t-@strip --strip-unneeded $(OUTDIR)/$(TARGET)" << newLine
|
||||
<< newLine;
|
||||
|
||||
for (int i = 0; i < files.size(); ++i)
|
||||
{
|
||||
if (shouldFileBeCompiledByDefault (files.getReference(i)))
|
||||
{
|
||||
jassert (files.getReference(i).getRoot() == RelativePath::buildTargetFolder);
|
||||
|
||||
out << "$(OBJDIR)/" << escapeSpaces (getObjectFileFor (files.getReference(i)))
|
||||
<< ": " << escapeSpaces (files.getReference(i).toUnixStyle()) << newLine
|
||||
<< "\t-@mkdir -p $(OBJDIR)" << newLine
|
||||
<< "\t@echo \"Compiling " << files.getReference(i).getFileName() << "\"" << newLine
|
||||
<< (files.getReference(i).hasFileExtension ("c;s;S") ? "\t@$(CC) $(CFLAGS) -o \"$@\" -c \"$<\""
|
||||
: "\t@$(CXX) $(CXXFLAGS) -o \"$@\" -c \"$<\"")
|
||||
<< newLine << newLine;
|
||||
}
|
||||
}
|
||||
|
||||
out << "-include $(OBJECTS:%.o=%.d)" << newLine;
|
||||
}
|
||||
|
||||
String getArchFlags (const BuildConfiguration& config) const
|
||||
{
|
||||
if (const MakeBuildConfiguration* makeConfig = dynamic_cast<const MakeBuildConfiguration*> (&config))
|
||||
if (makeConfig->getArchitectureTypeString().isNotEmpty())
|
||||
return makeConfig->getArchitectureTypeString();
|
||||
|
||||
return "-march=native";
|
||||
}
|
||||
|
||||
String getObjectFileFor (const RelativePath& file) const
|
||||
{
|
||||
return file.getFileNameWithoutExtension()
|
||||
+ "_" + String::toHexString (file.toUnixStyle().hashCode()) + ".o";
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (MakefileProjectExporter)
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,722 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "jucer_ProjectExporter.h"
|
||||
#include "jucer_ProjectSaver.h"
|
||||
|
||||
#include "jucer_ProjectExport_Make.h"
|
||||
#include "jucer_ProjectExport_MSVC.h"
|
||||
#include "jucer_ProjectExport_XCode.h"
|
||||
#include "jucer_ProjectExport_Android.h"
|
||||
#include "jucer_ProjectExport_CodeBlocks.h"
|
||||
|
||||
//==============================================================================
|
||||
static void addType (Array<ProjectExporter::ExporterTypeInfo>& list,
|
||||
const char* name, const void* iconData, int iconDataSize)
|
||||
{
|
||||
ProjectExporter::ExporterTypeInfo type = { name, iconData, iconDataSize };
|
||||
list.add (type);
|
||||
}
|
||||
|
||||
Array<ProjectExporter::ExporterTypeInfo> ProjectExporter::getExporterTypes()
|
||||
{
|
||||
Array<ProjectExporter::ExporterTypeInfo> types;
|
||||
|
||||
addType (types, XCodeProjectExporter::getNameMac(), BinaryData::projectIconXcode_png, BinaryData::projectIconXcode_pngSize);
|
||||
addType (types, XCodeProjectExporter::getNameiOS(), BinaryData::projectIconXcodeIOS_png, BinaryData::projectIconXcodeIOS_pngSize);
|
||||
addType (types, MSVCProjectExporterVC2015::getName(), BinaryData::projectIconVisualStudio_png, BinaryData::projectIconVisualStudio_pngSize);
|
||||
addType (types, MSVCProjectExporterVC2013::getName(), BinaryData::projectIconVisualStudio_png, BinaryData::projectIconVisualStudio_pngSize);
|
||||
addType (types, MSVCProjectExporterVC2012::getName(), BinaryData::projectIconVisualStudio_png, BinaryData::projectIconVisualStudio_pngSize);
|
||||
addType (types, MSVCProjectExporterVC2010::getName(), BinaryData::projectIconVisualStudio_png, BinaryData::projectIconVisualStudio_pngSize);
|
||||
addType (types, MSVCProjectExporterVC2008::getName(), BinaryData::projectIconVisualStudio_png, BinaryData::projectIconVisualStudio_pngSize);
|
||||
addType (types, MSVCProjectExporterVC2005::getName(), BinaryData::projectIconVisualStudio_png, BinaryData::projectIconVisualStudio_pngSize);
|
||||
addType (types, MakefileProjectExporter::getNameLinux(), BinaryData::projectIconLinuxMakefile_png, BinaryData::projectIconLinuxMakefile_pngSize);
|
||||
addType (types, AndroidProjectExporter::getNameAndroid(), BinaryData::projectIconAndroid_png, BinaryData::projectIconAndroid_pngSize);
|
||||
addType (types, CodeBlocksProjectExporter::getNameCodeBlocks(), BinaryData::projectIconCodeblocks_png, BinaryData::projectIconCodeblocks_pngSize);
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
ProjectExporter* ProjectExporter::createNewExporter (Project& project, const int index)
|
||||
{
|
||||
ProjectExporter* exp = nullptr;
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case 0: exp = new XCodeProjectExporter (project, ValueTree (XCodeProjectExporter ::getValueTreeTypeName (false)), false); break;
|
||||
case 1: exp = new XCodeProjectExporter (project, ValueTree (XCodeProjectExporter ::getValueTreeTypeName (true)), true); break;
|
||||
case 2: exp = new MSVCProjectExporterVC2015 (project, ValueTree (MSVCProjectExporterVC2015::getValueTreeTypeName())); break;
|
||||
case 3: exp = new MSVCProjectExporterVC2013 (project, ValueTree (MSVCProjectExporterVC2013::getValueTreeTypeName())); break;
|
||||
case 4: exp = new MSVCProjectExporterVC2012 (project, ValueTree (MSVCProjectExporterVC2012::getValueTreeTypeName())); break;
|
||||
case 5: exp = new MSVCProjectExporterVC2010 (project, ValueTree (MSVCProjectExporterVC2010::getValueTreeTypeName())); break;
|
||||
case 6: exp = new MSVCProjectExporterVC2008 (project, ValueTree (MSVCProjectExporterVC2008::getValueTreeTypeName())); break;
|
||||
case 7: exp = new MSVCProjectExporterVC2005 (project, ValueTree (MSVCProjectExporterVC2005::getValueTreeTypeName())); break;
|
||||
case 8: exp = new MakefileProjectExporter (project, ValueTree (MakefileProjectExporter ::getValueTreeTypeName())); break;
|
||||
case 9: exp = new AndroidProjectExporter (project, ValueTree (AndroidProjectExporter ::getValueTreeTypeName())); break;
|
||||
case 10: exp = new CodeBlocksProjectExporter (project, ValueTree (CodeBlocksProjectExporter::getValueTreeTypeName())); break;
|
||||
|
||||
default: jassertfalse; return 0;
|
||||
}
|
||||
|
||||
exp->createDefaultConfigs();
|
||||
exp->createDefaultModulePaths();
|
||||
|
||||
return exp;
|
||||
}
|
||||
|
||||
StringArray ProjectExporter::getExporterNames()
|
||||
{
|
||||
StringArray s;
|
||||
Array<ExporterTypeInfo> types (getExporterTypes());
|
||||
|
||||
for (int i = 0; i < types.size(); ++i)
|
||||
s.add (types.getReference(i).name);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
String ProjectExporter::getCurrentPlatformExporterName()
|
||||
{
|
||||
#if JUCE_MAC
|
||||
return XCodeProjectExporter::getNameMac();
|
||||
#elif JUCE_WINDOWS
|
||||
return MSVCProjectExporterVC2010::getName();
|
||||
#elif JUCE_LINUX
|
||||
return MakefileProjectExporter::getNameLinux();
|
||||
#else
|
||||
#error // huh?
|
||||
#endif
|
||||
}
|
||||
|
||||
ProjectExporter* ProjectExporter::createNewExporter (Project& project, const String& name)
|
||||
{
|
||||
return createNewExporter (project, getExporterNames().indexOf (name));
|
||||
}
|
||||
|
||||
ProjectExporter* ProjectExporter::createExporter (Project& project, const ValueTree& settings)
|
||||
{
|
||||
ProjectExporter* exp = MSVCProjectExporterVC2005::createForSettings (project, settings);
|
||||
if (exp == nullptr) exp = MSVCProjectExporterVC2008::createForSettings (project, settings);
|
||||
if (exp == nullptr) exp = MSVCProjectExporterVC2010::createForSettings (project, settings);
|
||||
if (exp == nullptr) exp = MSVCProjectExporterVC2012::createForSettings (project, settings);
|
||||
if (exp == nullptr) exp = MSVCProjectExporterVC2013::createForSettings (project, settings);
|
||||
if (exp == nullptr) exp = MSVCProjectExporterVC2015::createForSettings (project, settings);
|
||||
if (exp == nullptr) exp = XCodeProjectExporter ::createForSettings (project, settings);
|
||||
if (exp == nullptr) exp = MakefileProjectExporter ::createForSettings (project, settings);
|
||||
if (exp == nullptr) exp = AndroidProjectExporter ::createForSettings (project, settings);
|
||||
if (exp == nullptr) exp = CodeBlocksProjectExporter::createForSettings (project, settings);
|
||||
|
||||
jassert (exp != nullptr);
|
||||
return exp;
|
||||
}
|
||||
|
||||
bool ProjectExporter::canProjectBeLaunched (Project* project)
|
||||
{
|
||||
if (project != nullptr)
|
||||
{
|
||||
const char* types[] =
|
||||
{
|
||||
#if JUCE_MAC
|
||||
XCodeProjectExporter::getValueTreeTypeName (false),
|
||||
XCodeProjectExporter::getValueTreeTypeName (true),
|
||||
#elif JUCE_WINDOWS
|
||||
MSVCProjectExporterVC2005::getValueTreeTypeName(),
|
||||
MSVCProjectExporterVC2008::getValueTreeTypeName(),
|
||||
MSVCProjectExporterVC2010::getValueTreeTypeName(),
|
||||
MSVCProjectExporterVC2012::getValueTreeTypeName(),
|
||||
MSVCProjectExporterVC2013::getValueTreeTypeName(),
|
||||
MSVCProjectExporterVC2015::getValueTreeTypeName(),
|
||||
#elif JUCE_LINUX
|
||||
// (this doesn't currently launch.. not really sure what it would do on linux)
|
||||
//MakefileProjectExporter::getValueTreeTypeName(),
|
||||
#endif
|
||||
|
||||
nullptr
|
||||
};
|
||||
|
||||
for (const char** type = types; *type != nullptr; ++type)
|
||||
if (project->getExporters().getChildWithName (*type).isValid())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ProjectExporter::ProjectExporter (Project& p, const ValueTree& state)
|
||||
: xcodeIsBundle (false),
|
||||
xcodeCreatePList (false),
|
||||
xcodeCanUseDwarf (true),
|
||||
makefileIsDLL (false),
|
||||
msvcIsDLL (false),
|
||||
msvcIsWindowsSubsystem (true),
|
||||
settings (state),
|
||||
project (p),
|
||||
projectType (p.getProjectType()),
|
||||
projectName (p.getTitle()),
|
||||
projectFolder (p.getProjectFolder()),
|
||||
modulesGroup (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
ProjectExporter::~ProjectExporter()
|
||||
{
|
||||
}
|
||||
|
||||
File ProjectExporter::getTargetFolder() const
|
||||
{
|
||||
return project.resolveFilename (getTargetLocationString());
|
||||
}
|
||||
|
||||
RelativePath ProjectExporter::rebaseFromProjectFolderToBuildTarget (const RelativePath& path) const
|
||||
{
|
||||
return path.rebased (project.getProjectFolder(), getTargetFolder(), RelativePath::buildTargetFolder);
|
||||
}
|
||||
|
||||
bool ProjectExporter::shouldFileBeCompiledByDefault (const RelativePath& file) const
|
||||
{
|
||||
return file.hasFileExtension (cOrCppFileExtensions)
|
||||
|| file.hasFileExtension (asmFileExtensions);
|
||||
}
|
||||
|
||||
void ProjectExporter::createPropertyEditors (PropertyListBuilder& props)
|
||||
{
|
||||
props.add (new TextPropertyComponent (getTargetLocationValue(), "Target Project Folder", 2048, false),
|
||||
"The location of the folder in which the " + name + " project will be created. "
|
||||
"This path can be absolute, but it's much more sensible to make it relative to the jucer project directory.");
|
||||
|
||||
OwnedArray<LibraryModule> modules;
|
||||
project.getModules().createRequiredModules (modules);
|
||||
|
||||
for (int i = 0; i < modules.size(); ++i)
|
||||
modules.getUnchecked(i)->createPropertyEditors (*this, props);
|
||||
|
||||
props.add (new TextPropertyComponent (getExporterPreprocessorDefs(), "Extra Preprocessor Definitions", 32768, true),
|
||||
"Extra preprocessor definitions. Use the form \"NAME1=value NAME2=value\", using whitespace, commas, "
|
||||
"or new-lines to separate the items - to include a space or comma in a definition, precede it with a backslash.");
|
||||
|
||||
props.add (new TextPropertyComponent (getExtraCompilerFlags(), "Extra compiler flags", 8192, 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 (getExtraLinkerFlags(), "Extra linker flags", 8192, true),
|
||||
"Extra command-line flags to be passed to the linker. You might want to use this for adding additional libraries. "
|
||||
"This string can contain references to preprocessor definitions in the form ${NAME_OF_VALUE}, which will be replaced with their values.");
|
||||
|
||||
props.add (new TextPropertyComponent (getExternalLibraries(), "External libraries to link", 8192, true),
|
||||
"Additional libraries to link (one per line). You should not add any platform specific decoration to these names. "
|
||||
"This string can contain references to preprocessor definitions in the form ${NAME_OF_VALUE}, which will be replaced with their values.");
|
||||
|
||||
{
|
||||
OwnedArray<Project::Item> images;
|
||||
project.findAllImageItems (images);
|
||||
|
||||
StringArray choices;
|
||||
Array<var> ids;
|
||||
|
||||
choices.add ("<None>");
|
||||
ids.add (var::null);
|
||||
choices.add (String::empty);
|
||||
ids.add (var::null);
|
||||
|
||||
for (int i = 0; i < images.size(); ++i)
|
||||
{
|
||||
choices.add (images.getUnchecked(i)->getName());
|
||||
ids.add (images.getUnchecked(i)->getID());
|
||||
}
|
||||
|
||||
props.add (new ChoicePropertyComponent (getSmallIconImageItemID(), "Icon (small)", choices, ids),
|
||||
"Sets an icon to use for the executable.");
|
||||
|
||||
props.add (new ChoicePropertyComponent (getBigIconImageItemID(), "Icon (large)", choices, ids),
|
||||
"Sets an icon to use for the executable.");
|
||||
}
|
||||
|
||||
createExporterProperties (props);
|
||||
|
||||
props.add (new TextPropertyComponent (getUserNotes(), "Notes", 32768, true),
|
||||
"Extra comments: This field is not used for code or project generation, it's just a space where you can express your thoughts.");
|
||||
}
|
||||
|
||||
StringPairArray ProjectExporter::getAllPreprocessorDefs (const ProjectExporter::BuildConfiguration& config) const
|
||||
{
|
||||
StringPairArray defs (mergePreprocessorDefs (config.getAllPreprocessorDefs(),
|
||||
parsePreprocessorDefs (getExporterPreprocessorDefsString())));
|
||||
addDefaultPreprocessorDefs (defs);
|
||||
return defs;
|
||||
}
|
||||
|
||||
StringPairArray ProjectExporter::getAllPreprocessorDefs() const
|
||||
{
|
||||
StringPairArray defs (mergePreprocessorDefs (project.getPreprocessorDefs(),
|
||||
parsePreprocessorDefs (getExporterPreprocessorDefsString())));
|
||||
addDefaultPreprocessorDefs (defs);
|
||||
return defs;
|
||||
}
|
||||
|
||||
void ProjectExporter::addDefaultPreprocessorDefs (StringPairArray& defs) const
|
||||
{
|
||||
defs.set (getExporterIdentifierMacro(), "1");
|
||||
defs.set ("JUCE_APP_VERSION", project.getVersionString());
|
||||
defs.set ("JUCE_APP_VERSION_HEX", project.getVersionAsHex());
|
||||
}
|
||||
|
||||
String ProjectExporter::replacePreprocessorTokens (const ProjectExporter::BuildConfiguration& config, const String& sourceString) const
|
||||
{
|
||||
return replacePreprocessorDefs (getAllPreprocessorDefs (config), sourceString);
|
||||
}
|
||||
|
||||
void ProjectExporter::copyMainGroupFromProject()
|
||||
{
|
||||
jassert (itemGroups.size() == 0);
|
||||
itemGroups.add (project.getMainGroup().createCopy());
|
||||
}
|
||||
|
||||
Project::Item& ProjectExporter::getModulesGroup()
|
||||
{
|
||||
if (modulesGroup == nullptr)
|
||||
{
|
||||
jassert (itemGroups.size() > 0); // must call copyMainGroupFromProject before this.
|
||||
itemGroups.add (Project::Item::createGroup (project, "Juce Modules", "__modulesgroup__"));
|
||||
modulesGroup = &(itemGroups.getReference (itemGroups.size() - 1));
|
||||
}
|
||||
|
||||
return *modulesGroup;
|
||||
}
|
||||
|
||||
void ProjectExporter::addToExtraSearchPaths (const RelativePath& pathFromProjectFolder)
|
||||
{
|
||||
RelativePath localPath (rebaseFromProjectFolderToBuildTarget (pathFromProjectFolder));
|
||||
|
||||
const String path (isVisualStudio() ? localPath.toWindowsStyle() : localPath.toUnixStyle());
|
||||
extraSearchPaths.addIfNotAlreadyThere (path, false);
|
||||
}
|
||||
|
||||
Value ProjectExporter::getPathForModuleValue (const String& moduleID)
|
||||
{
|
||||
UndoManager* um = project.getUndoManagerFor (settings);
|
||||
|
||||
ValueTree paths (settings.getOrCreateChildWithName (Ids::MODULEPATHS, um));
|
||||
ValueTree m (paths.getChildWithProperty (Ids::ID, moduleID));
|
||||
|
||||
if (! m.isValid())
|
||||
{
|
||||
m = ValueTree (Ids::MODULEPATH);
|
||||
m.setProperty (Ids::ID, moduleID, um);
|
||||
paths.addChild (m, -1, um);
|
||||
}
|
||||
|
||||
return m.getPropertyAsValue (Ids::path, um);
|
||||
}
|
||||
|
||||
String ProjectExporter::getPathForModuleString (const String& moduleID) const
|
||||
{
|
||||
return settings.getChildWithName (Ids::MODULEPATHS)
|
||||
.getChildWithProperty (Ids::ID, moduleID) [Ids::path].toString();
|
||||
}
|
||||
|
||||
void ProjectExporter::removePathForModule (const String& moduleID)
|
||||
{
|
||||
ValueTree paths (settings.getChildWithName (Ids::MODULEPATHS));
|
||||
ValueTree m (paths.getChildWithProperty (Ids::ID, moduleID));
|
||||
paths.removeChild (m, project.getUndoManagerFor (settings));
|
||||
}
|
||||
|
||||
RelativePath ProjectExporter::getModuleFolderRelativeToProject (const String& moduleID, ProjectSaver& projectSaver) const
|
||||
{
|
||||
if (project.getModules().shouldCopyModuleFilesLocally (moduleID).getValue())
|
||||
return RelativePath (project.getRelativePathForFile (projectSaver.getLocalModuleFolder (moduleID)),
|
||||
RelativePath::projectFolder);
|
||||
|
||||
String path (getPathForModuleString (moduleID));
|
||||
|
||||
if (path.isEmpty())
|
||||
return getLegacyModulePath (moduleID).getChildFile (moduleID);
|
||||
|
||||
return RelativePath (path, RelativePath::projectFolder).getChildFile (moduleID);
|
||||
}
|
||||
|
||||
String ProjectExporter::getLegacyModulePath() const
|
||||
{
|
||||
return getSettingString ("juceFolder");
|
||||
}
|
||||
|
||||
RelativePath ProjectExporter::getLegacyModulePath (const String& moduleID) const
|
||||
{
|
||||
if (project.getModules().state.getChildWithProperty (Ids::ID, moduleID) ["useLocalCopy"])
|
||||
return RelativePath (project.getRelativePathForFile (project.getGeneratedCodeFolder()
|
||||
.getChildFile ("modules")
|
||||
.getChildFile (moduleID)), RelativePath::projectFolder);
|
||||
|
||||
String oldJucePath (getLegacyModulePath());
|
||||
|
||||
if (oldJucePath.isEmpty())
|
||||
return RelativePath();
|
||||
|
||||
RelativePath p (oldJucePath, RelativePath::projectFolder);
|
||||
if (p.getFileName() != "modules")
|
||||
p = p.getChildFile ("modules");
|
||||
|
||||
return p.getChildFile (moduleID);
|
||||
}
|
||||
|
||||
void ProjectExporter::updateOldModulePaths()
|
||||
{
|
||||
String oldPath (getLegacyModulePath());
|
||||
|
||||
if (oldPath.isNotEmpty())
|
||||
{
|
||||
for (int i = project.getModules().getNumModules(); --i >= 0;)
|
||||
{
|
||||
String modID (project.getModules().getModuleID(i));
|
||||
getPathForModuleValue (modID) = getLegacyModulePath (modID).getParentDirectory().toUnixStyle();
|
||||
}
|
||||
|
||||
settings.removeProperty ("juceFolder", nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
static bool areCompatibleExporters (const ProjectExporter& p1, const ProjectExporter& p2)
|
||||
{
|
||||
return (p1.isVisualStudio() && p2.isVisualStudio())
|
||||
|| (p1.isXcode() && p2.isXcode())
|
||||
|| (p1.isLinux() && p2.isLinux())
|
||||
|| (p1.isAndroid() && p2.isAndroid())
|
||||
|| (p1.isCodeBlocks() && p2.isCodeBlocks());
|
||||
}
|
||||
|
||||
void ProjectExporter::createDefaultModulePaths()
|
||||
{
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
{
|
||||
if (areCompatibleExporters (*this, *exporter))
|
||||
{
|
||||
for (int i = project.getModules().getNumModules(); --i >= 0;)
|
||||
{
|
||||
String modID (project.getModules().getModuleID(i));
|
||||
getPathForModuleValue (modID) = exporter->getPathForModuleValue (modID).getValue();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
{
|
||||
if (exporter->canLaunchProject())
|
||||
{
|
||||
for (int i = project.getModules().getNumModules(); --i >= 0;)
|
||||
{
|
||||
String modID (project.getModules().getModuleID(i));
|
||||
getPathForModuleValue (modID) = exporter->getPathForModuleValue (modID).getValue();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = project.getModules().getNumModules(); --i >= 0;)
|
||||
{
|
||||
String modID (project.getModules().getModuleID(i));
|
||||
getPathForModuleValue (modID) = "../../juce";
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ValueTree ProjectExporter::getConfigurations() const
|
||||
{
|
||||
return settings.getChildWithName (Ids::CONFIGURATIONS);
|
||||
}
|
||||
|
||||
int ProjectExporter::getNumConfigurations() const
|
||||
{
|
||||
return getConfigurations().getNumChildren();
|
||||
}
|
||||
|
||||
ProjectExporter::BuildConfiguration::Ptr ProjectExporter::getConfiguration (int index) const
|
||||
{
|
||||
return createBuildConfig (getConfigurations().getChild (index));
|
||||
}
|
||||
|
||||
bool ProjectExporter::hasConfigurationNamed (const String& nameToFind) const
|
||||
{
|
||||
const ValueTree configs (getConfigurations());
|
||||
for (int i = configs.getNumChildren(); --i >= 0;)
|
||||
if (configs.getChild(i) [Ids::name].toString() == nameToFind)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
String ProjectExporter::getUniqueConfigName (String nm) const
|
||||
{
|
||||
String nameRoot (nm);
|
||||
while (CharacterFunctions::isDigit (nameRoot.getLastCharacter()))
|
||||
nameRoot = nameRoot.dropLastCharacters (1);
|
||||
|
||||
nameRoot = nameRoot.trim();
|
||||
|
||||
int suffix = 2;
|
||||
while (hasConfigurationNamed (name))
|
||||
nm = nameRoot + " " + String (suffix++);
|
||||
|
||||
return nm;
|
||||
}
|
||||
|
||||
void ProjectExporter::addNewConfiguration (const BuildConfiguration* configToCopy)
|
||||
{
|
||||
const String configName (getUniqueConfigName (configToCopy != nullptr ? configToCopy->config [Ids::name].toString()
|
||||
: "New Build Configuration"));
|
||||
|
||||
ValueTree configs (getConfigurations());
|
||||
|
||||
if (! configs.isValid())
|
||||
{
|
||||
settings.addChild (ValueTree (Ids::CONFIGURATIONS), 0, project.getUndoManagerFor (settings));
|
||||
configs = getConfigurations();
|
||||
}
|
||||
|
||||
ValueTree newConfig (Ids::CONFIGURATION);
|
||||
if (configToCopy != nullptr)
|
||||
newConfig = configToCopy->config.createCopy();
|
||||
|
||||
newConfig.setProperty (Ids::name, configName, 0);
|
||||
|
||||
configs.addChild (newConfig, -1, project.getUndoManagerFor (configs));
|
||||
}
|
||||
|
||||
void ProjectExporter::BuildConfiguration::removeFromExporter()
|
||||
{
|
||||
ValueTree configs (config.getParent());
|
||||
configs.removeChild (config, project.getUndoManagerFor (configs));
|
||||
}
|
||||
|
||||
void ProjectExporter::createDefaultConfigs()
|
||||
{
|
||||
settings.getOrCreateChildWithName (Ids::CONFIGURATIONS, nullptr);
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
addNewConfiguration (nullptr);
|
||||
BuildConfiguration::Ptr config (getConfiguration (i));
|
||||
|
||||
const bool debugConfig = i == 0;
|
||||
|
||||
config->getNameValue() = debugConfig ? "Debug" : "Release";
|
||||
config->isDebugValue() = debugConfig;
|
||||
config->getOptimisationLevel() = debugConfig ? optimisationOff : optimiseMinSize;
|
||||
config->getTargetBinaryName() = project.getProjectFilenameRoot();
|
||||
}
|
||||
}
|
||||
|
||||
Drawable* ProjectExporter::getBigIcon() const
|
||||
{
|
||||
return project.getMainGroup().findItemWithID (settings [Ids::bigIcon]).loadAsImageFile();
|
||||
}
|
||||
|
||||
Drawable* ProjectExporter::getSmallIcon() const
|
||||
{
|
||||
return project.getMainGroup().findItemWithID (settings [Ids::smallIcon]).loadAsImageFile();
|
||||
}
|
||||
|
||||
Image ProjectExporter::getBestIconForSize (int size, bool returnNullIfNothingBigEnough) const
|
||||
{
|
||||
Drawable* im = nullptr;
|
||||
|
||||
ScopedPointer<Drawable> im1 (getSmallIcon());
|
||||
ScopedPointer<Drawable> im2 (getBigIcon());
|
||||
|
||||
if (im1 != nullptr && im2 != nullptr)
|
||||
{
|
||||
if (im1->getWidth() >= size && im2->getWidth() >= size)
|
||||
im = im1->getWidth() < im2->getWidth() ? im1 : im2;
|
||||
else if (im1->getWidth() >= size)
|
||||
im = im1;
|
||||
else if (im2->getWidth() >= size)
|
||||
im = im2;
|
||||
}
|
||||
else
|
||||
{
|
||||
im = im1 != nullptr ? im1 : im2;
|
||||
}
|
||||
|
||||
if (im == nullptr)
|
||||
return Image();
|
||||
|
||||
if (returnNullIfNothingBigEnough && im->getWidth() < size && im->getHeight() < size)
|
||||
return Image();
|
||||
|
||||
return rescaleImageForIcon (*im, size);
|
||||
}
|
||||
|
||||
Image ProjectExporter::rescaleImageForIcon (Drawable& d, const int size)
|
||||
{
|
||||
if (DrawableImage* drawableImage = dynamic_cast<DrawableImage*> (&d))
|
||||
{
|
||||
Image im = SoftwareImageType().convert (drawableImage->getImage());
|
||||
|
||||
if (size == im.getWidth() && size == im.getHeight())
|
||||
return im;
|
||||
|
||||
// (scale it down in stages for better resampling)
|
||||
while (im.getWidth() > 2 * size && im.getHeight() > 2 * size)
|
||||
im = im.rescaled (im.getWidth() / 2,
|
||||
im.getHeight() / 2);
|
||||
|
||||
Image newIm (Image::ARGB, size, size, true, SoftwareImageType());
|
||||
Graphics g (newIm);
|
||||
g.drawImageWithin (im, 0, 0, size, size,
|
||||
RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false);
|
||||
return newIm;
|
||||
}
|
||||
|
||||
Image im (Image::ARGB, size, size, true, SoftwareImageType());
|
||||
Graphics g (im);
|
||||
d.drawWithin (g, im.getBounds().toFloat(), RectanglePlacement::centred, 1.0f);
|
||||
return im;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
ProjectExporter::ConfigIterator::ConfigIterator (ProjectExporter& e)
|
||||
: index (-1), exporter (e)
|
||||
{
|
||||
}
|
||||
|
||||
bool ProjectExporter::ConfigIterator::next()
|
||||
{
|
||||
if (++index >= exporter.getNumConfigurations())
|
||||
return false;
|
||||
|
||||
config = exporter.getConfiguration (index);
|
||||
return true;
|
||||
}
|
||||
|
||||
ProjectExporter::ConstConfigIterator::ConstConfigIterator (const ProjectExporter& exporter_)
|
||||
: index (-1), exporter (exporter_)
|
||||
{
|
||||
}
|
||||
|
||||
bool ProjectExporter::ConstConfigIterator::next()
|
||||
{
|
||||
if (++index >= exporter.getNumConfigurations())
|
||||
return false;
|
||||
|
||||
config = exporter.getConfiguration (index);
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ProjectExporter::BuildConfiguration::BuildConfiguration (Project& p, const ValueTree& configNode)
|
||||
: config (configNode), project (p)
|
||||
{
|
||||
}
|
||||
|
||||
ProjectExporter::BuildConfiguration::~BuildConfiguration()
|
||||
{
|
||||
}
|
||||
|
||||
String ProjectExporter::BuildConfiguration::getGCCOptimisationFlag() const
|
||||
{
|
||||
switch (getOptimisationLevelInt())
|
||||
{
|
||||
case optimiseMaxSpeed: return "3";
|
||||
case optimiseMinSize: return "s";
|
||||
default: return "0";
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectExporter::BuildConfiguration::createPropertyEditors (PropertyListBuilder& props)
|
||||
{
|
||||
props.add (new TextPropertyComponent (getNameValue(), "Name", 96, false),
|
||||
"The name of this configuration.");
|
||||
|
||||
props.add (new BooleanPropertyComponent (isDebugValue(), "Debug mode", "Debugging enabled"),
|
||||
"If enabled, this means that the configuration should be built with debug synbols.");
|
||||
|
||||
static const char* optimisationLevels[] = { "No optimisation", "Minimise size", "Maximise speed", 0 };
|
||||
const int optimisationLevelValues[] = { optimisationOff, optimiseMinSize, optimiseMaxSpeed, 0 };
|
||||
props.add (new ChoicePropertyComponent (getOptimisationLevel(), "Optimisation",
|
||||
StringArray (optimisationLevels), Array<var> (optimisationLevelValues)),
|
||||
"The optimisation level for this configuration");
|
||||
|
||||
props.add (new TextPropertyComponent (getTargetBinaryName(), "Binary name", 256, false),
|
||||
"The filename to use for the destination binary executable file. If you don't add a suffix to this name, "
|
||||
"a suitable platform-specific suffix will be added automatically.");
|
||||
|
||||
props.add (new TextPropertyComponent (getTargetBinaryRelativePath(), "Binary location", 1024, false),
|
||||
"The folder in which the finished binary should be placed. Leave this blank to cause the binary to be placed "
|
||||
"in its default location in the build folder.");
|
||||
|
||||
props.addSearchPathProperty (getHeaderSearchPathValue(), "Header search paths", "Extra header search paths.");
|
||||
props.addSearchPathProperty (getLibrarySearchPathValue(), "Extra library search paths", "Extra library search paths.");
|
||||
|
||||
props.add (new TextPropertyComponent (getBuildConfigPreprocessorDefs(), "Preprocessor definitions", 32768, true),
|
||||
"Extra preprocessor definitions. Use the form \"NAME1=value NAME2=value\", using whitespace, commas, or "
|
||||
"new-lines to separate the items - to include a space or comma in a definition, precede it with a backslash.");
|
||||
|
||||
createConfigProperties (props);
|
||||
|
||||
props.add (new TextPropertyComponent (getUserNotes(), "Notes", 32768, true),
|
||||
"Extra comments: This field is not used for code or project generation, it's just a space where you can express your thoughts.");
|
||||
}
|
||||
|
||||
StringPairArray ProjectExporter::BuildConfiguration::getAllPreprocessorDefs() const
|
||||
{
|
||||
return mergePreprocessorDefs (project.getPreprocessorDefs(),
|
||||
parsePreprocessorDefs (getBuildConfigPreprocessorDefsString()));
|
||||
}
|
||||
|
||||
StringArray ProjectExporter::BuildConfiguration::getHeaderSearchPaths() const
|
||||
{
|
||||
return getSearchPathsFromString (getHeaderSearchPathString());
|
||||
}
|
||||
|
||||
StringArray ProjectExporter::BuildConfiguration::getLibrarySearchPaths() const
|
||||
{
|
||||
return getSearchPathsFromString (getLibrarySearchPathString());
|
||||
}
|
||||
|
||||
String ProjectExporter::BuildConfiguration::getGCCLibraryPathFlags() const
|
||||
{
|
||||
String s;
|
||||
const StringArray libraryPaths (getLibrarySearchPaths());
|
||||
|
||||
for (int i = 0; i < libraryPaths.size(); ++i)
|
||||
s << " -L" << addQuotesIfContainsSpaces (libraryPaths[i]);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
String ProjectExporter::getExternalLibraryFlags (const BuildConfiguration& config) const
|
||||
{
|
||||
StringArray libraries;
|
||||
libraries.addTokens (getExternalLibrariesString(), ";\n", "\"'");
|
||||
libraries.removeEmptyStrings (true);
|
||||
|
||||
if (libraries.size() != 0)
|
||||
return replacePreprocessorTokens (config, "-l" + libraries.joinIntoString (" -l")).trim();
|
||||
|
||||
return String::empty;
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_PROJECTEXPORTER_JUCEHEADER__
|
||||
#define __JUCER_PROJECTEXPORTER_JUCEHEADER__
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "../Project/jucer_Project.h"
|
||||
#include "../Project/jucer_ProjectType.h"
|
||||
class ProjectSaver;
|
||||
|
||||
//==============================================================================
|
||||
class ProjectExporter
|
||||
{
|
||||
public:
|
||||
ProjectExporter (Project&, const ValueTree& settings);
|
||||
virtual ~ProjectExporter();
|
||||
|
||||
struct ExporterTypeInfo
|
||||
{
|
||||
String name;
|
||||
const void* iconData;
|
||||
int iconDataSize;
|
||||
};
|
||||
|
||||
static StringArray getExporterNames();
|
||||
static Array<ExporterTypeInfo> getExporterTypes();
|
||||
|
||||
static ProjectExporter* createNewExporter (Project&, const int index);
|
||||
static ProjectExporter* createNewExporter (Project&, const String& name);
|
||||
static ProjectExporter* createExporter (Project&, const ValueTree& settings);
|
||||
static bool canProjectBeLaunched (Project*);
|
||||
|
||||
static String getCurrentPlatformExporterName();
|
||||
|
||||
//=============================================================================
|
||||
virtual bool usesMMFiles() const = 0;
|
||||
virtual void createExporterProperties (PropertyListBuilder&) = 0;
|
||||
virtual bool canLaunchProject() = 0;
|
||||
virtual bool launchProject() = 0;
|
||||
virtual void create (const OwnedArray<LibraryModule>&) const = 0; // may throw a SaveError
|
||||
virtual bool shouldFileBeCompiledByDefault (const RelativePath& path) const;
|
||||
virtual bool canCopeWithDuplicateFiles() = 0;
|
||||
|
||||
virtual bool isXcode() const { return false; }
|
||||
virtual bool isVisualStudio() const { return false; }
|
||||
virtual int getVisualStudioVersion() const { return 0; }
|
||||
virtual bool isCodeBlocks() const { return false; }
|
||||
|
||||
virtual bool isAndroid() const { return false; }
|
||||
virtual bool isWindows() const { return false; }
|
||||
virtual bool isLinux() const { return false; }
|
||||
virtual bool isOSX() const { return false; }
|
||||
|
||||
bool mayCompileOnCurrentOS() const
|
||||
{
|
||||
#if JUCE_MAC
|
||||
return isOSX() || isAndroid();
|
||||
#elif JUCE_WINDOWS
|
||||
return isWindows() || isAndroid();
|
||||
#elif JUCE_LINUX
|
||||
return isLinux() || isAndroid();
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String getName() const { return name; }
|
||||
File getTargetFolder() const;
|
||||
|
||||
Project& getProject() noexcept { return project; }
|
||||
const Project& getProject() const noexcept { return project; }
|
||||
|
||||
Value getSetting (const Identifier& nm) { return settings.getPropertyAsValue (nm, project.getUndoManagerFor (settings)); }
|
||||
String getSettingString (const Identifier& nm) const { return settings [nm]; }
|
||||
|
||||
Value getTargetLocationValue() { return getSetting (Ids::targetFolder); }
|
||||
String getTargetLocationString() const { return getSettingString (Ids::targetFolder); }
|
||||
|
||||
Value getExtraCompilerFlags() { return getSetting (Ids::extraCompilerFlags); }
|
||||
String getExtraCompilerFlagsString() const { return getSettingString (Ids::extraCompilerFlags).replaceCharacters ("\r\n", " "); }
|
||||
|
||||
Value getExtraLinkerFlags() { return getSetting (Ids::extraLinkerFlags); }
|
||||
String getExtraLinkerFlagsString() const { return getSettingString (Ids::extraLinkerFlags).replaceCharacters ("\r\n", " "); }
|
||||
|
||||
Value getExternalLibraries() { return getSetting (Ids::externalLibraries); }
|
||||
String getExternalLibrariesString() const { return getSettingString (Ids::externalLibraries).replaceCharacters ("\r\n", " ;"); }
|
||||
|
||||
Value getUserNotes() { return getSetting (Ids::userNotes); }
|
||||
|
||||
// NB: this is the path to the parent "modules" folder that contains the named module, not the
|
||||
// module folder itself.
|
||||
Value getPathForModuleValue (const String& moduleID);
|
||||
String getPathForModuleString (const String& moduleID) const;
|
||||
void removePathForModule (const String& moduleID);
|
||||
|
||||
RelativePath getLegacyModulePath (const String& moduleID) const;
|
||||
String getLegacyModulePath() const;
|
||||
|
||||
// Returns a path to the actual module folder itself
|
||||
RelativePath getModuleFolderRelativeToProject (const String& moduleID, ProjectSaver& projectSaver) const;
|
||||
void updateOldModulePaths();
|
||||
|
||||
RelativePath rebaseFromProjectFolderToBuildTarget (const RelativePath& path) const;
|
||||
void addToExtraSearchPaths (const RelativePath& pathFromProjectFolder);
|
||||
|
||||
Value getBigIconImageItemID() { return getSetting (Ids::bigIcon); }
|
||||
Value getSmallIconImageItemID() { return getSetting (Ids::smallIcon); }
|
||||
Drawable* getBigIcon() const;
|
||||
Drawable* getSmallIcon() const;
|
||||
Image getBestIconForSize (int size, bool returnNullIfNothingBigEnough) const;
|
||||
|
||||
String getExporterIdentifierMacro() const
|
||||
{
|
||||
return "JUCER_" + settings.getType().toString() + "_"
|
||||
+ String::toHexString (getSettingString (Ids::targetFolder).hashCode()).toUpperCase();
|
||||
}
|
||||
|
||||
// An exception that can be thrown by the create() method.
|
||||
class SaveError
|
||||
{
|
||||
public:
|
||||
SaveError (const String& error) : message (error)
|
||||
{}
|
||||
|
||||
SaveError (const File& fileThatFailedToWrite)
|
||||
: message ("Can't write to the file: " + fileThatFailedToWrite.getFullPathName())
|
||||
{}
|
||||
|
||||
String message;
|
||||
};
|
||||
|
||||
void createPropertyEditors (PropertyListBuilder& props);
|
||||
|
||||
//==============================================================================
|
||||
void copyMainGroupFromProject();
|
||||
Array<Project::Item>& getAllGroups() noexcept { jassert (itemGroups.size() > 0); return itemGroups; }
|
||||
const Array<Project::Item>& getAllGroups() const noexcept { jassert (itemGroups.size() > 0); return itemGroups; }
|
||||
Project::Item& getModulesGroup();
|
||||
|
||||
//==============================================================================
|
||||
String xcodePackageType, xcodeBundleSignature, xcodeBundleExtension;
|
||||
String xcodeProductType, xcodeProductInstallPath, xcodeFileType;
|
||||
String xcodeOtherRezFlags, xcodeExcludedFiles64Bit;
|
||||
bool xcodeIsBundle, xcodeCreatePList, xcodeCanUseDwarf;
|
||||
StringArray xcodeFrameworks;
|
||||
Array<RelativePath> xcodeExtraLibrariesDebug, xcodeExtraLibrariesRelease;
|
||||
Array<XmlElement> xcodeExtraPListEntries;
|
||||
|
||||
//==============================================================================
|
||||
String makefileTargetSuffix;
|
||||
bool makefileIsDLL;
|
||||
StringArray linuxLibs;
|
||||
|
||||
//==============================================================================
|
||||
String msvcTargetSuffix;
|
||||
StringPairArray msvcExtraPreprocessorDefs;
|
||||
bool msvcIsDLL, msvcIsWindowsSubsystem;
|
||||
String msvcDelayLoadedDLLs;
|
||||
StringArray mingwLibs;
|
||||
|
||||
//==============================================================================
|
||||
StringArray extraSearchPaths;
|
||||
|
||||
//==============================================================================
|
||||
class BuildConfiguration : public ReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
BuildConfiguration (Project& project, const ValueTree& configNode);
|
||||
~BuildConfiguration();
|
||||
|
||||
typedef ReferenceCountedObjectPtr<BuildConfiguration> Ptr;
|
||||
|
||||
//==============================================================================
|
||||
virtual void createConfigProperties (PropertyListBuilder&) = 0;
|
||||
|
||||
//==============================================================================
|
||||
Value getNameValue() { return getValue (Ids::name); }
|
||||
String getName() const { return config [Ids::name]; }
|
||||
|
||||
Value isDebugValue() { return getValue (Ids::isDebug); }
|
||||
bool isDebug() const { return config [Ids::isDebug]; }
|
||||
|
||||
Value getTargetBinaryName() { return getValue (Ids::targetName); }
|
||||
String getTargetBinaryNameString() const { return config [Ids::targetName]; }
|
||||
|
||||
// the path relative to the build folder in which the binary should go
|
||||
Value getTargetBinaryRelativePath() { return getValue (Ids::binaryPath); }
|
||||
String getTargetBinaryRelativePathString() const { return config [Ids::binaryPath]; }
|
||||
|
||||
Value getOptimisationLevel() { return getValue (Ids::optimisation); }
|
||||
int getOptimisationLevelInt() const { return config [Ids::optimisation]; }
|
||||
String getGCCOptimisationFlag() const;
|
||||
|
||||
Value getBuildConfigPreprocessorDefs() { return getValue (Ids::defines); }
|
||||
String getBuildConfigPreprocessorDefsString() const { return config [Ids::defines]; }
|
||||
StringPairArray getAllPreprocessorDefs() const; // includes inherited definitions
|
||||
|
||||
Value getHeaderSearchPathValue() { return getValue (Ids::headerPath); }
|
||||
String getHeaderSearchPathString() const { return config [Ids::headerPath]; }
|
||||
StringArray getHeaderSearchPaths() const;
|
||||
|
||||
Value getLibrarySearchPathValue() { return getValue (Ids::libraryPath); }
|
||||
String getLibrarySearchPathString() const { return config [Ids::libraryPath]; }
|
||||
StringArray getLibrarySearchPaths() const;
|
||||
String getGCCLibraryPathFlags() const;
|
||||
|
||||
Value getUserNotes() { return getValue (Ids::userNotes); }
|
||||
|
||||
Value getValue (const Identifier& nm) { return config.getPropertyAsValue (nm, getUndoManager()); }
|
||||
UndoManager* getUndoManager() const { return project.getUndoManagerFor (config); }
|
||||
|
||||
void createPropertyEditors (PropertyListBuilder&);
|
||||
void removeFromExporter();
|
||||
|
||||
//==============================================================================
|
||||
ValueTree config;
|
||||
Project& project;
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BuildConfiguration)
|
||||
};
|
||||
|
||||
void addNewConfiguration (const BuildConfiguration* configToCopy);
|
||||
bool hasConfigurationNamed (const String& name) const;
|
||||
String getUniqueConfigName (String name) const;
|
||||
|
||||
String getExternalLibraryFlags (const BuildConfiguration& config) const;
|
||||
|
||||
//==============================================================================
|
||||
struct ConfigIterator
|
||||
{
|
||||
ConfigIterator (ProjectExporter& exporter);
|
||||
|
||||
bool next();
|
||||
|
||||
BuildConfiguration& operator*() const { return *config; }
|
||||
BuildConfiguration* operator->() const { return config; }
|
||||
|
||||
BuildConfiguration::Ptr config;
|
||||
int index;
|
||||
|
||||
private:
|
||||
ProjectExporter& exporter;
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConfigIterator)
|
||||
};
|
||||
|
||||
struct ConstConfigIterator
|
||||
{
|
||||
ConstConfigIterator (const ProjectExporter& exporter);
|
||||
|
||||
bool next();
|
||||
|
||||
const BuildConfiguration& operator*() const { return *config; }
|
||||
const BuildConfiguration* operator->() const { return config; }
|
||||
|
||||
BuildConfiguration::Ptr config;
|
||||
int index;
|
||||
|
||||
private:
|
||||
const ProjectExporter& exporter;
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConstConfigIterator)
|
||||
};
|
||||
|
||||
int getNumConfigurations() const;
|
||||
BuildConfiguration::Ptr getConfiguration (int index) const;
|
||||
|
||||
ValueTree getConfigurations() const;
|
||||
void createDefaultConfigs();
|
||||
void createDefaultModulePaths();
|
||||
|
||||
//==============================================================================
|
||||
Value getExporterPreprocessorDefs() { return getSetting (Ids::extraDefs); }
|
||||
String getExporterPreprocessorDefsString() const { return getSettingString (Ids::extraDefs); }
|
||||
|
||||
// includes exporter, project + config defs
|
||||
StringPairArray getAllPreprocessorDefs (const BuildConfiguration& config) const;
|
||||
// includes exporter + project defs..
|
||||
StringPairArray getAllPreprocessorDefs() const;
|
||||
|
||||
String replacePreprocessorTokens (const BuildConfiguration&, const String& sourceString) const;
|
||||
|
||||
ValueTree settings;
|
||||
|
||||
//==============================================================================
|
||||
enum OptimisationLevel
|
||||
{
|
||||
optimisationOff = 1,
|
||||
optimiseMinSize = 2,
|
||||
optimiseMaxSpeed = 3
|
||||
};
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
String name;
|
||||
Project& project;
|
||||
const ProjectType& projectType;
|
||||
const String projectName;
|
||||
const File projectFolder;
|
||||
|
||||
mutable Array<Project::Item> itemGroups;
|
||||
void initItemGroups() const;
|
||||
Project::Item* modulesGroup;
|
||||
|
||||
virtual BuildConfiguration::Ptr createBuildConfig (const ValueTree&) const = 0;
|
||||
|
||||
void addDefaultPreprocessorDefs (StringPairArray&) const;
|
||||
|
||||
static String getDefaultBuildsRootFolder() { return "Builds/"; }
|
||||
|
||||
static String getLibbedFilename (String name)
|
||||
{
|
||||
if (! name.startsWith ("lib")) name = "lib" + name;
|
||||
if (! name.endsWithIgnoreCase (".a")) name += ".a";
|
||||
return name;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static void overwriteFileIfDifferentOrThrow (const File& file, const MemoryOutputStream& newData)
|
||||
{
|
||||
if (! FileHelpers::overwriteFileWithNewDataIfDifferent (file, newData))
|
||||
throw SaveError (file);
|
||||
}
|
||||
|
||||
static void overwriteFileIfDifferentOrThrow (const File& file, const String& newData)
|
||||
{
|
||||
if (! FileHelpers::overwriteFileWithNewDataIfDifferent (file, newData))
|
||||
throw SaveError (file);
|
||||
}
|
||||
|
||||
static void createDirectoryOrThrow (const File& dirToCreate)
|
||||
{
|
||||
if (! dirToCreate.createDirectory())
|
||||
throw SaveError ("Can't create folder: " + dirToCreate.getFullPathName());
|
||||
}
|
||||
|
||||
static void writeXmlOrThrow (const XmlElement& xml, const File& file, const String& encoding, int maxCharsPerLine, bool useUnixNewLines = false)
|
||||
{
|
||||
MemoryOutputStream mo;
|
||||
xml.writeToStream (mo, String::empty, false, true, encoding, maxCharsPerLine);
|
||||
|
||||
if (useUnixNewLines)
|
||||
{
|
||||
MemoryOutputStream mo2;
|
||||
mo2 << mo.toString().replace ("\r\n", "\n");
|
||||
overwriteFileIfDifferentOrThrow (file, mo2);
|
||||
}
|
||||
else
|
||||
{
|
||||
overwriteFileIfDifferentOrThrow (file, mo);
|
||||
}
|
||||
}
|
||||
|
||||
static Image rescaleImageForIcon (Drawable&, int iconSize);
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectExporter)
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_PROJECTEXPORTER_JUCEHEADER__
|
||||
@@ -0,0 +1,606 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_PROJECTSAVER_JUCEHEADER__
|
||||
#define __JUCER_PROJECTSAVER_JUCEHEADER__
|
||||
|
||||
#include "jucer_ResourceFile.h"
|
||||
#include "../Project/jucer_Module.h"
|
||||
#include "jucer_ProjectExporter.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ProjectSaver
|
||||
{
|
||||
public:
|
||||
ProjectSaver (Project& p, const File& file)
|
||||
: project (p),
|
||||
projectFile (file),
|
||||
generatedCodeFolder (project.getGeneratedCodeFolder()),
|
||||
generatedFilesGroup (Project::Item::createGroup (project, getJuceCodeGroupName(), "__generatedcode__")),
|
||||
hasBinaryData (false)
|
||||
{
|
||||
generatedFilesGroup.setID (getGeneratedGroupID());
|
||||
}
|
||||
|
||||
struct SaveThread : public ThreadWithProgressWindow
|
||||
{
|
||||
public:
|
||||
SaveThread (ProjectSaver& ps)
|
||||
: ThreadWithProgressWindow ("Saving...", true, false),
|
||||
saver (ps), result (Result::ok())
|
||||
{}
|
||||
|
||||
void run() override
|
||||
{
|
||||
setProgress (-1);
|
||||
result = saver.save (false);
|
||||
}
|
||||
|
||||
ProjectSaver& saver;
|
||||
Result result;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (SaveThread)
|
||||
};
|
||||
|
||||
Result save (bool showProgressBox)
|
||||
{
|
||||
if (showProgressBox)
|
||||
{
|
||||
SaveThread thread (*this);
|
||||
thread.runThread();
|
||||
return thread.result;
|
||||
}
|
||||
|
||||
const String appConfigUserContent (loadUserContentFromAppConfig());
|
||||
|
||||
const File oldFile (project.getFile());
|
||||
project.setFile (projectFile);
|
||||
|
||||
writeMainProjectFile();
|
||||
|
||||
OwnedArray<LibraryModule> modules;
|
||||
project.getModules().createRequiredModules (modules);
|
||||
|
||||
if (errors.size() == 0) writeAppConfigFile (modules, appConfigUserContent);
|
||||
if (errors.size() == 0) writeBinaryDataFiles();
|
||||
if (errors.size() == 0) writeAppHeader (modules);
|
||||
if (errors.size() == 0) writeProjects (modules);
|
||||
if (errors.size() == 0) writeAppConfigFile (modules, appConfigUserContent); // (this is repeated in case the projects added anything to it)
|
||||
|
||||
if (errors.size() == 0 && generatedCodeFolder.exists())
|
||||
writeReadmeFile();
|
||||
|
||||
if (generatedCodeFolder.exists())
|
||||
deleteUnwantedFilesIn (generatedCodeFolder);
|
||||
|
||||
if (errors.size() > 0)
|
||||
{
|
||||
project.setFile (oldFile);
|
||||
return Result::fail (errors[0]);
|
||||
}
|
||||
|
||||
return Result::ok();
|
||||
}
|
||||
|
||||
Result saveResourcesOnly()
|
||||
{
|
||||
writeBinaryDataFiles();
|
||||
|
||||
if (errors.size() > 0)
|
||||
return Result::fail (errors[0]);
|
||||
|
||||
return Result::ok();
|
||||
}
|
||||
|
||||
Project::Item saveGeneratedFile (const String& filePath, const MemoryOutputStream& newData)
|
||||
{
|
||||
if (! generatedCodeFolder.createDirectory())
|
||||
{
|
||||
addError ("Couldn't create folder: " + generatedCodeFolder.getFullPathName());
|
||||
return Project::Item (project, ValueTree::invalid);
|
||||
}
|
||||
|
||||
const File file (generatedCodeFolder.getChildFile (filePath));
|
||||
|
||||
if (replaceFileIfDifferent (file, newData))
|
||||
return addFileToGeneratedGroup (file);
|
||||
|
||||
return Project::Item (project, ValueTree::invalid);
|
||||
}
|
||||
|
||||
Project::Item addFileToGeneratedGroup (const File& file)
|
||||
{
|
||||
Project::Item item (generatedFilesGroup.findItemForFile (file));
|
||||
|
||||
if (item.isValid())
|
||||
return item;
|
||||
|
||||
generatedFilesGroup.addFile (file, -1, true);
|
||||
return generatedFilesGroup.findItemForFile (file);
|
||||
}
|
||||
|
||||
void setExtraAppConfigFileContent (const String& content)
|
||||
{
|
||||
extraAppConfigContent = content;
|
||||
}
|
||||
|
||||
static void writeAutoGenWarningComment (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;
|
||||
}
|
||||
|
||||
static const char* getGeneratedGroupID() noexcept { return "__jucelibfiles"; }
|
||||
Project::Item& getGeneratedCodeGroup() { return generatedFilesGroup; }
|
||||
|
||||
static String getJuceCodeGroupName() { return "Juce Library Code"; }
|
||||
|
||||
File getGeneratedCodeFolder() const { return generatedCodeFolder; }
|
||||
File getLocalModulesFolder() const { return generatedCodeFolder.getChildFile ("modules"); }
|
||||
File getLocalModuleFolder (const String& moduleID) const { return getLocalModulesFolder().getChildFile (moduleID); }
|
||||
|
||||
bool replaceFileIfDifferent (const File& f, const MemoryOutputStream& newData)
|
||||
{
|
||||
filesCreated.add (f);
|
||||
|
||||
if (! FileHelpers::overwriteFileWithNewDataIfDifferent (f, newData))
|
||||
{
|
||||
addError ("Can't write to file: " + f.getFullPathName());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool copyFolder (const File& source, const File& dest)
|
||||
{
|
||||
if (source.isDirectory() && dest.createDirectory())
|
||||
{
|
||||
Array<File> subFiles;
|
||||
source.findChildFiles (subFiles, File::findFiles, false);
|
||||
|
||||
for (int i = 0; i < subFiles.size(); ++i)
|
||||
{
|
||||
const File target (dest.getChildFile (subFiles.getReference(i).getFileName()));
|
||||
filesCreated.add (target);
|
||||
if (! subFiles.getReference(i).copyFileTo (target))
|
||||
return false;
|
||||
}
|
||||
|
||||
subFiles.clear();
|
||||
source.findChildFiles (subFiles, File::findDirectories, false);
|
||||
|
||||
for (int i = 0; i < subFiles.size(); ++i)
|
||||
if (! copyFolder (subFiles.getReference(i), dest.getChildFile (subFiles.getReference(i).getFileName())))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Project& project;
|
||||
|
||||
private:
|
||||
const File projectFile, generatedCodeFolder;
|
||||
Project::Item generatedFilesGroup;
|
||||
String extraAppConfigContent;
|
||||
StringArray errors;
|
||||
CriticalSection errorLock;
|
||||
|
||||
File appConfigFile;
|
||||
SortedSet<File> filesCreated;
|
||||
bool hasBinaryData;
|
||||
|
||||
// Recursively clears out any files in a folder that we didn't create, but avoids
|
||||
// any folders containing hidden files that might be used by version-control systems.
|
||||
bool deleteUnwantedFilesIn (const File& parent)
|
||||
{
|
||||
bool folderIsNowEmpty = true;
|
||||
DirectoryIterator i (parent, false, "*", File::findFilesAndDirectories);
|
||||
Array<File> filesToDelete;
|
||||
|
||||
bool isFolder;
|
||||
while (i.next (&isFolder, nullptr, nullptr, nullptr, nullptr, nullptr))
|
||||
{
|
||||
const File f (i.getFile());
|
||||
|
||||
if (filesCreated.contains (f) || shouldFileBeKept (f.getFileName()))
|
||||
{
|
||||
folderIsNowEmpty = false;
|
||||
}
|
||||
else if (isFolder)
|
||||
{
|
||||
if (deleteUnwantedFilesIn (f))
|
||||
filesToDelete.add (f);
|
||||
else
|
||||
folderIsNowEmpty = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
filesToDelete.add (f);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = filesToDelete.size(); --j >= 0;)
|
||||
filesToDelete.getReference(j).deleteRecursively();
|
||||
|
||||
return folderIsNowEmpty;
|
||||
}
|
||||
|
||||
static bool shouldFileBeKept (const String& filename)
|
||||
{
|
||||
static const char* filesToKeep[] = { ".svn", ".cvs", "CMakeLists.txt" };
|
||||
|
||||
for (int i = 0; i < numElementsInArray (filesToKeep); ++i)
|
||||
if (filename == filesToKeep[i])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void writeMainProjectFile()
|
||||
{
|
||||
ScopedPointer <XmlElement> xml (project.getProjectRoot().createXml());
|
||||
jassert (xml != nullptr);
|
||||
|
||||
if (xml != nullptr)
|
||||
{
|
||||
MemoryOutputStream mo;
|
||||
xml->writeToStream (mo, String::empty);
|
||||
replaceFileIfDifferent (projectFile, mo);
|
||||
}
|
||||
}
|
||||
|
||||
static int findLongestModuleName (const OwnedArray<LibraryModule>& modules)
|
||||
{
|
||||
int longest = 0;
|
||||
|
||||
for (int i = modules.size(); --i >= 0;)
|
||||
longest = jmax (longest, modules.getUnchecked(i)->getID().length());
|
||||
|
||||
return longest;
|
||||
}
|
||||
|
||||
File getAppConfigFile() const { return generatedCodeFolder.getChildFile (project.getAppConfigFilename()); }
|
||||
|
||||
String loadUserContentFromAppConfig() const
|
||||
{
|
||||
StringArray lines, userContent;
|
||||
lines.addLines (getAppConfigFile().loadFileAsString());
|
||||
bool foundCodeSection = false;
|
||||
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
if (lines[i].contains ("[BEGIN_USER_CODE_SECTION]"))
|
||||
{
|
||||
for (int j = i + 1; j < lines.size() && ! lines[j].contains ("[END_USER_CODE_SECTION]"); ++j)
|
||||
userContent.add (lines[j]);
|
||||
|
||||
foundCodeSection = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! foundCodeSection)
|
||||
{
|
||||
userContent.add (String::empty);
|
||||
userContent.add ("// (You can add your own code in this section, and the Introjucer will not overwrite it)");
|
||||
userContent.add (String::empty);
|
||||
}
|
||||
|
||||
return userContent.joinIntoString (newLine) + newLine;
|
||||
}
|
||||
|
||||
void writeAppConfig (OutputStream& out, const OwnedArray<LibraryModule>& modules, const String& userContent)
|
||||
{
|
||||
writeAutoGenWarningComment (out);
|
||||
out << " There's a section below where you can add your own custom code safely, and the" << newLine
|
||||
<< " Introjucer will preserve the contents of that block, but the best way to change" << newLine
|
||||
<< " any of these definitions is by using the Introjucer's project settings." << newLine
|
||||
<< newLine
|
||||
<< " Any commented-out settings will assume their default values." << newLine
|
||||
<< newLine
|
||||
<< "*/" << newLine
|
||||
<< newLine;
|
||||
|
||||
const String headerGuard ("__JUCE_APPCONFIG_" + project.getProjectUID().toUpperCase() + "__");
|
||||
out << "#ifndef " << headerGuard << newLine
|
||||
<< "#define " << headerGuard << newLine
|
||||
<< newLine
|
||||
<< "//==============================================================================" << newLine
|
||||
<< "// [BEGIN_USER_CODE_SECTION]" << newLine
|
||||
<< userContent
|
||||
<< "// [END_USER_CODE_SECTION]" << newLine
|
||||
<< newLine
|
||||
<< "//==============================================================================" << newLine;
|
||||
|
||||
const int longestName = findLongestModuleName (modules);
|
||||
|
||||
for (int k = 0; k < modules.size(); ++k)
|
||||
{
|
||||
LibraryModule* const m = modules.getUnchecked(k);
|
||||
out << "#define JUCE_MODULE_AVAILABLE_" << m->getID()
|
||||
<< String::repeatedString (" ", longestName + 5 - m->getID().length()) << " 1" << newLine;
|
||||
}
|
||||
|
||||
out << newLine;
|
||||
|
||||
for (int j = 0; j < modules.size(); ++j)
|
||||
{
|
||||
LibraryModule* const m = modules.getUnchecked(j);
|
||||
OwnedArray<Project::ConfigFlag> flags;
|
||||
m->getConfigFlags (project, flags);
|
||||
|
||||
if (flags.size() > 0)
|
||||
{
|
||||
out << "//==============================================================================" << newLine
|
||||
<< "// " << m->getID() << " flags:" << newLine
|
||||
<< newLine;
|
||||
|
||||
for (int i = 0; i < flags.size(); ++i)
|
||||
{
|
||||
flags.getUnchecked(i)->value.referTo (project.getConfigFlag (flags.getUnchecked(i)->symbol));
|
||||
|
||||
const Project::ConfigFlag* const f = flags[i];
|
||||
const String value (project.getConfigFlag (f->symbol).toString());
|
||||
|
||||
out << "#ifndef " << f->symbol << newLine;
|
||||
|
||||
if (value == Project::configFlagEnabled)
|
||||
out << " #define " << f->symbol << " 1";
|
||||
else if (value == Project::configFlagDisabled)
|
||||
out << " #define " << f->symbol << " 0";
|
||||
else
|
||||
out << " //#define " << f->symbol;
|
||||
|
||||
out << newLine
|
||||
<< "#endif" << newLine
|
||||
<< newLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (extraAppConfigContent.isNotEmpty())
|
||||
out << newLine << extraAppConfigContent.trimEnd() << newLine;
|
||||
|
||||
out << newLine
|
||||
<< "#endif // " << headerGuard << newLine;
|
||||
}
|
||||
|
||||
void writeAppConfigFile (const OwnedArray<LibraryModule>& modules, const String& userContent)
|
||||
{
|
||||
appConfigFile = getAppConfigFile();
|
||||
|
||||
MemoryOutputStream mem;
|
||||
writeAppConfig (mem, modules, userContent);
|
||||
saveGeneratedFile (project.getAppConfigFilename(), mem);
|
||||
}
|
||||
|
||||
void writeAppHeader (OutputStream& out, const OwnedArray<LibraryModule>& modules)
|
||||
{
|
||||
writeAutoGenWarningComment (out);
|
||||
|
||||
out << " This is the header file that your files should include in order to get all the" << newLine
|
||||
<< " JUCE library headers. You should avoid including the JUCE headers directly in" << newLine
|
||||
<< " your own source files, because that wouldn't pick up the correct configuration" << newLine
|
||||
<< " options for your app." << newLine
|
||||
<< newLine
|
||||
<< "*/" << newLine << newLine;
|
||||
|
||||
String headerGuard ("__APPHEADERFILE_" + project.getProjectUID().toUpperCase() + "__");
|
||||
out << "#ifndef " << headerGuard << newLine
|
||||
<< "#define " << headerGuard << newLine << newLine;
|
||||
|
||||
if (appConfigFile.exists())
|
||||
out << CodeHelpers::createIncludeStatement (project.getAppConfigFilename()) << newLine;
|
||||
|
||||
for (int i = 0; i < modules.size(); ++i)
|
||||
modules.getUnchecked(i)->writeIncludes (*this, out);
|
||||
|
||||
if (hasBinaryData && project.shouldIncludeBinaryInAppConfig().getValue())
|
||||
out << CodeHelpers::createIncludeStatement (project.getBinaryDataHeaderFile(), appConfigFile) << newLine;
|
||||
|
||||
out << newLine
|
||||
<< "#if ! DONT_SET_USING_JUCE_NAMESPACE" << newLine
|
||||
<< " // If your code uses a lot of JUCE classes, then this will obviously save you" << newLine
|
||||
<< " // a lot of typing, but can be disabled by setting DONT_SET_USING_JUCE_NAMESPACE." << newLine
|
||||
<< " using namespace juce;" << newLine
|
||||
<< "#endif" << newLine
|
||||
<< newLine
|
||||
<< "#if ! JUCE_DONT_DECLARE_PROJECTINFO" << newLine
|
||||
<< "namespace ProjectInfo" << newLine
|
||||
<< "{" << newLine
|
||||
<< " const char* const projectName = " << CppTokeniserFunctions::addEscapeChars (project.getTitle()).quoted() << ";" << newLine
|
||||
<< " const char* const versionString = " << CppTokeniserFunctions::addEscapeChars (project.getVersionString()).quoted() << ";" << newLine
|
||||
<< " const int versionNumber = " << project.getVersionAsHex() << ";" << newLine
|
||||
<< "}" << newLine
|
||||
<< "#endif" << newLine
|
||||
<< newLine
|
||||
<< "#endif // " << headerGuard << newLine;
|
||||
}
|
||||
|
||||
void writeAppHeader (const OwnedArray<LibraryModule>& modules)
|
||||
{
|
||||
MemoryOutputStream mem;
|
||||
writeAppHeader (mem, modules);
|
||||
saveGeneratedFile (project.getJuceSourceHFilename(), mem);
|
||||
}
|
||||
|
||||
void writeBinaryDataFiles()
|
||||
{
|
||||
const File binaryDataH (project.getBinaryDataHeaderFile());
|
||||
|
||||
ResourceFile resourceFile (project);
|
||||
|
||||
if (resourceFile.getNumFiles() > 0)
|
||||
{
|
||||
resourceFile.setClassName ("BinaryData");
|
||||
|
||||
Array<File> binaryDataFiles;
|
||||
|
||||
int maxSize = project.getMaxBinaryFileSize().getValue();
|
||||
if (maxSize <= 0)
|
||||
maxSize = 10 * 1024 * 1024;
|
||||
|
||||
if (resourceFile.write (binaryDataFiles, maxSize))
|
||||
{
|
||||
hasBinaryData = true;
|
||||
|
||||
for (int i = 0; i < binaryDataFiles.size(); ++i)
|
||||
{
|
||||
const File& f = binaryDataFiles.getReference(i);
|
||||
|
||||
filesCreated.add (f);
|
||||
generatedFilesGroup.addFile (f, -1, ! f.hasFileExtension (".h"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
addError ("Can't create binary resources file: "
|
||||
+ project.getBinaryDataCppFile(0).getFullPathName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 20; --i >= 0;)
|
||||
project.getBinaryDataCppFile (i).deleteFile();
|
||||
|
||||
binaryDataH.deleteFile();
|
||||
}
|
||||
}
|
||||
|
||||
void writeReadmeFile()
|
||||
{
|
||||
MemoryOutputStream out;
|
||||
out << newLine
|
||||
<< " Important Note!!" << newLine
|
||||
<< " ================" << newLine
|
||||
<< newLine
|
||||
<< "The purpose of this folder is to contain files that are auto-generated by the Introjucer," << newLine
|
||||
<< "and ALL files in this folder will be mercilessly DELETED and completely re-written whenever" << newLine
|
||||
<< "the Introjucer saves your project." << newLine
|
||||
<< newLine
|
||||
<< "Therefore, it's a bad idea to make any manual changes to the files in here, or to" << newLine
|
||||
<< "put any of your own files in here if you don't want to lose them. (Of course you may choose" << newLine
|
||||
<< "to add the folder's contents to your version-control system so that you can re-merge your own" << newLine
|
||||
<< "modifications after the Introjucer has saved its changes)." << newLine;
|
||||
|
||||
replaceFileIfDifferent (generatedCodeFolder.getChildFile ("ReadMe.txt"), out);
|
||||
}
|
||||
|
||||
static void sortGroupRecursively (Project::Item group)
|
||||
{
|
||||
group.sortAlphabetically (true);
|
||||
|
||||
for (int i = group.getNumChildren(); --i >= 0;)
|
||||
sortGroupRecursively (group.getChild(i));
|
||||
}
|
||||
|
||||
void addError (const String& message)
|
||||
{
|
||||
const ScopedLock sl (errorLock);
|
||||
errors.add (message);
|
||||
}
|
||||
|
||||
void writeProjects (const OwnedArray<LibraryModule>& modules)
|
||||
{
|
||||
ThreadPool threadPool;
|
||||
|
||||
// keep a copy of the basic generated files group, as each exporter may modify it.
|
||||
const ValueTree originalGeneratedGroup (generatedFilesGroup.state.createCopy());
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
{
|
||||
if (exporter->getTargetFolder().createDirectory())
|
||||
{
|
||||
exporter->copyMainGroupFromProject();
|
||||
exporter->settings = exporter->settings.createCopy();
|
||||
|
||||
exporter->addToExtraSearchPaths (RelativePath ("JuceLibraryCode", RelativePath::projectFolder));
|
||||
|
||||
generatedFilesGroup.state = originalGeneratedGroup.createCopy();
|
||||
project.getProjectType().prepareExporter (*exporter);
|
||||
|
||||
for (int j = 0; j < modules.size(); ++j)
|
||||
modules.getUnchecked(j)->prepareExporter (*exporter, *this);
|
||||
|
||||
sortGroupRecursively (generatedFilesGroup);
|
||||
exporter->getAllGroups().add (generatedFilesGroup);
|
||||
|
||||
threadPool.addJob (new ExporterJob (*this, exporter.exporter.release(), modules), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
addError ("Can't create folder: " + exporter->getTargetFolder().getFullPathName());
|
||||
}
|
||||
}
|
||||
|
||||
while (threadPool.getNumJobs() > 0)
|
||||
Thread::sleep (10);
|
||||
}
|
||||
|
||||
class ExporterJob : public ThreadPoolJob
|
||||
{
|
||||
public:
|
||||
ExporterJob (ProjectSaver& ps, ProjectExporter* pe,
|
||||
const OwnedArray<LibraryModule>& moduleList)
|
||||
: ThreadPoolJob ("export"),
|
||||
owner (ps), exporter (pe), modules (moduleList)
|
||||
{
|
||||
}
|
||||
|
||||
JobStatus runJob()
|
||||
{
|
||||
try
|
||||
{
|
||||
exporter->create (modules);
|
||||
std::cout << "Finished saving: " << exporter->getName() << std::endl;
|
||||
}
|
||||
catch (ProjectExporter::SaveError& error)
|
||||
{
|
||||
owner.addError (error.message);
|
||||
}
|
||||
|
||||
return jobHasFinished;
|
||||
}
|
||||
|
||||
private:
|
||||
ProjectSaver& owner;
|
||||
ScopedPointer<ProjectExporter> exporter;
|
||||
const OwnedArray<LibraryModule>& modules;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExporterJob)
|
||||
};
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectSaver)
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_PROJECTSAVER_JUCEHEADER__
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "jucer_ResourceFile.h"
|
||||
#include "../Application/jucer_OpenDocumentManager.h"
|
||||
|
||||
static const char* resourceFileIdentifierString = "JUCER_BINARY_RESOURCE";
|
||||
|
||||
|
||||
//==============================================================================
|
||||
ResourceFile::ResourceFile (Project& p)
|
||||
: project (p),
|
||||
className ("BinaryData")
|
||||
{
|
||||
addResourcesFromProjectItem (project.getMainGroup());
|
||||
}
|
||||
|
||||
ResourceFile::~ResourceFile()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ResourceFile::addResourcesFromProjectItem (const Project::Item& projectItem)
|
||||
{
|
||||
if (projectItem.isGroup())
|
||||
{
|
||||
for (int i = 0; i < projectItem.getNumChildren(); ++i)
|
||||
addResourcesFromProjectItem (projectItem.getChild(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (projectItem.shouldBeAddedToBinaryResources())
|
||||
addFile (projectItem.getFile());
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ResourceFile::setClassName (const String& name)
|
||||
{
|
||||
className = name;
|
||||
}
|
||||
|
||||
void ResourceFile::addFile (const File& file)
|
||||
{
|
||||
files.add (file);
|
||||
|
||||
const String variableNameRoot (CodeHelpers::makeBinaryDataIdentifierName (file));
|
||||
String variableName (variableNameRoot);
|
||||
|
||||
int suffix = 2;
|
||||
while (variableNames.contains (variableName))
|
||||
variableName = variableNameRoot + String (suffix++);
|
||||
|
||||
variableNames.add (variableName);
|
||||
}
|
||||
|
||||
String ResourceFile::getDataVariableFor (const File& file) const
|
||||
{
|
||||
jassert (files.indexOf (file) >= 0);
|
||||
return variableNames [files.indexOf (file)];
|
||||
}
|
||||
|
||||
String ResourceFile::getSizeVariableFor (const File& file) const
|
||||
{
|
||||
jassert (files.indexOf (file) >= 0);
|
||||
return variableNames [files.indexOf (file)] + "Size";
|
||||
}
|
||||
|
||||
int64 ResourceFile::getTotalDataSize() const
|
||||
{
|
||||
int64 total = 0;
|
||||
|
||||
for (int i = 0; i < files.size(); ++i)
|
||||
total += files.getReference(i).getSize();
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static String getComment()
|
||||
{
|
||||
String comment;
|
||||
comment << newLine << newLine
|
||||
<< " This is an auto-generated file: Any edits you make may be overwritten!" << newLine
|
||||
<< newLine
|
||||
<< "*/" << newLine
|
||||
<< newLine;
|
||||
|
||||
return comment;
|
||||
}
|
||||
|
||||
bool ResourceFile::writeHeader (MemoryOutputStream& header)
|
||||
{
|
||||
const String headerGuard ("BINARYDATA_H_" + String (project.getProjectUID().hashCode() & 0x7ffffff) + "_INCLUDED");
|
||||
|
||||
header << "/* ========================================================================================="
|
||||
<< getComment()
|
||||
<< "#ifndef " << headerGuard << newLine
|
||||
<< "#define " << headerGuard << newLine
|
||||
<< newLine
|
||||
<< "namespace " << className << newLine
|
||||
<< "{" << newLine;
|
||||
|
||||
bool containsAnyImages = false;
|
||||
|
||||
for (int i = 0; i < files.size(); ++i)
|
||||
{
|
||||
const File& file = files.getReference(i);
|
||||
const int64 dataSize = file.getSize();
|
||||
|
||||
const String variableName (variableNames[i]);
|
||||
|
||||
FileInputStream fileStream (file);
|
||||
|
||||
if (fileStream.openedOk())
|
||||
{
|
||||
containsAnyImages = containsAnyImages
|
||||
|| (ImageFileFormat::findImageFormatForStream (fileStream) != nullptr);
|
||||
|
||||
header << " extern const char* " << variableName << ";" << newLine;
|
||||
header << " const int " << variableName << "Size = " << (int) dataSize << ";" << newLine << newLine;
|
||||
}
|
||||
}
|
||||
|
||||
header << " // Points to the start of a list of resource names." << newLine
|
||||
<< " extern const char* namedResourceList[];" << newLine
|
||||
<< newLine
|
||||
<< " // Number of elements in the namedResourceList array." << newLine
|
||||
<< " const int namedResourceListSize = " << files.size() << ";" << newLine
|
||||
<< newLine
|
||||
<< " // If you provide the name of one of the binary resource variables above, this function will" << newLine
|
||||
<< " // return the corresponding data and its size (or a null pointer if the name isn't found)." << newLine
|
||||
<< " const char* getNamedResource (const char* resourceNameUTF8, int& dataSizeInBytes) throw();" << newLine
|
||||
<< "}" << newLine
|
||||
<< newLine
|
||||
<< "#endif" << newLine;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceFile::writeCpp (MemoryOutputStream& cpp, const File& headerFile, int& i, const int maxFileSize)
|
||||
{
|
||||
const bool isFirstFile = (i == 0);
|
||||
|
||||
cpp << "/* ==================================== " << resourceFileIdentifierString << " ===================================="
|
||||
<< getComment()
|
||||
<< "namespace " << className << newLine
|
||||
<< "{" << newLine;
|
||||
|
||||
bool containsAnyImages = false;
|
||||
|
||||
while (i < files.size())
|
||||
{
|
||||
const File& file = files.getReference(i);
|
||||
const String variableName (variableNames[i]);
|
||||
|
||||
FileInputStream fileStream (file);
|
||||
|
||||
if (fileStream.openedOk())
|
||||
{
|
||||
containsAnyImages = containsAnyImages
|
||||
|| (ImageFileFormat::findImageFormatForStream (fileStream) != nullptr);
|
||||
|
||||
const String tempVariable ("temp_binary_data_" + String (i));
|
||||
|
||||
cpp << newLine << "//================== " << file.getFileName() << " ==================" << newLine
|
||||
<< "static const unsigned char " << tempVariable << "[] =" << newLine;
|
||||
|
||||
{
|
||||
MemoryBlock data;
|
||||
fileStream.readIntoMemoryBlock (data);
|
||||
CodeHelpers::writeDataAsCppLiteral (data, cpp, true, true);
|
||||
}
|
||||
|
||||
cpp << newLine << newLine
|
||||
<< "const char* " << variableName << " = (const char*) " << tempVariable << ";" << newLine;
|
||||
}
|
||||
|
||||
++i;
|
||||
|
||||
if (cpp.getPosition() > maxFileSize)
|
||||
break;
|
||||
}
|
||||
|
||||
if (isFirstFile)
|
||||
{
|
||||
if (i < files.size())
|
||||
{
|
||||
cpp << newLine
|
||||
<< "}" << newLine
|
||||
<< newLine
|
||||
<< "#include \"" << headerFile.getFileName() << "\"" << newLine
|
||||
<< newLine
|
||||
<< "namespace " << className << newLine
|
||||
<< "{";
|
||||
}
|
||||
|
||||
cpp << newLine
|
||||
<< newLine
|
||||
<< "const char* getNamedResource (const char*, int&) throw();" << newLine
|
||||
<< "const char* getNamedResource (const char* resourceNameUTF8, int& numBytes) throw()" << newLine
|
||||
<< "{" << newLine;
|
||||
|
||||
StringArray returnCodes;
|
||||
for (int j = 0; j < files.size(); ++j)
|
||||
{
|
||||
const File& file = files.getReference(j);
|
||||
const int64 dataSize = file.getSize();
|
||||
returnCodes.add ("numBytes = " + String (dataSize) + "; return " + variableNames[j] + ";");
|
||||
}
|
||||
|
||||
CodeHelpers::createStringMatcher (cpp, "resourceNameUTF8", variableNames, returnCodes, 4);
|
||||
|
||||
cpp << " numBytes = 0;" << newLine
|
||||
<< " return 0;" << newLine
|
||||
<< "}" << newLine
|
||||
<< newLine
|
||||
<< "const char* namedResourceList[] =" << newLine
|
||||
<< "{" << newLine;
|
||||
|
||||
for (int j = 0; j < files.size(); ++j)
|
||||
cpp << " " << variableNames[j].quoted() << (j < files.size() - 1 ? "," : "") << newLine;
|
||||
|
||||
cpp << "};" << newLine;
|
||||
}
|
||||
|
||||
cpp << newLine
|
||||
<< "}" << newLine;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceFile::write (Array<File>& filesCreated, const int maxFileSize)
|
||||
{
|
||||
const File headerFile (project.getBinaryDataHeaderFile());
|
||||
|
||||
{
|
||||
MemoryOutputStream mo;
|
||||
if (! (writeHeader (mo) && FileHelpers::overwriteFileWithNewDataIfDifferent (headerFile, mo)))
|
||||
return false;
|
||||
|
||||
filesCreated.add (headerFile);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
int fileIndex = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
File cpp (project.getBinaryDataCppFile (fileIndex));
|
||||
|
||||
MemoryOutputStream mo;
|
||||
if (! (writeCpp (mo, headerFile, i, maxFileSize) && FileHelpers::overwriteFileWithNewDataIfDifferent (cpp, mo)))
|
||||
return false;
|
||||
|
||||
filesCreated.add (cpp);
|
||||
++fileIndex;
|
||||
|
||||
if (i >= files.size())
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_RESOURCEFILE_JUCEHEADER__
|
||||
#define __JUCER_RESOURCEFILE_JUCEHEADER__
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "../Project/jucer_Project.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ResourceFile
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
ResourceFile (Project& project);
|
||||
~ResourceFile();
|
||||
|
||||
//==============================================================================
|
||||
void setClassName (const String& className);
|
||||
String getClassName() const { return className; }
|
||||
|
||||
void addFile (const File& file);
|
||||
String getDataVariableFor (const File& file) const;
|
||||
String getSizeVariableFor (const File& file) const;
|
||||
|
||||
int getNumFiles() const { return files.size(); }
|
||||
int64 getTotalDataSize() const;
|
||||
|
||||
bool write (Array<File>& filesCreated, int maxFileSize);
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
Array<File> files;
|
||||
StringArray variableNames;
|
||||
Project& project;
|
||||
String className;
|
||||
|
||||
bool writeHeader (MemoryOutputStream&);
|
||||
bool writeCpp (MemoryOutputStream&, const File& headerFile, int& index, int maxFileSize);
|
||||
void addResourcesFromProjectItem (const Project::Item& node);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResourceFile)
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_RESOURCEFILE_JUCEHEADER__
|
||||
Reference in New Issue
Block a user