@@ -429,7 +429,7 @@ | |||||
<Option compile="0"/> | <Option compile="0"/> | ||||
<Option link="0"/> | <Option link="0"/> | ||||
</Unit> | </Unit> | ||||
<Unit filename="../../Source/Project Saving/jucer_ProjectExport_Android.h"> | |||||
<Unit filename="../../Source/Project Saving/jucer_ProjectExport_AndroidBase.h"> | |||||
<Option compile="0"/> | <Option compile="0"/> | ||||
<Option link="0"/> | <Option link="0"/> | ||||
</Unit> | </Unit> | ||||
@@ -437,6 +437,10 @@ | |||||
<Option compile="0"/> | <Option compile="0"/> | ||||
<Option link="0"/> | <Option link="0"/> | ||||
</Unit> | </Unit> | ||||
<Unit filename="../../Source/Project Saving/jucer_ProjectExport_AndroidAnt.h"> | |||||
<Option compile="0"/> | |||||
<Option link="0"/> | |||||
</Unit> | |||||
<Unit filename="../../Source/Project Saving/jucer_ProjectExport_CodeBlocks.h"> | <Unit filename="../../Source/Project Saving/jucer_ProjectExport_CodeBlocks.h"> | ||||
<Option compile="0"/> | <Option compile="0"/> | ||||
<Option link="0"/> | <Option link="0"/> | ||||
@@ -85,6 +85,7 @@ | |||||
045B9C5D21C5C86FED140D81 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_mac_CoreGraphicsHelpers.h"; path = "../../../../modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h"; sourceTree = "SOURCE_ROOT"; }; | 045B9C5D21C5C86FED140D81 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_mac_CoreGraphicsHelpers.h"; path = "../../../../modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h"; sourceTree = "SOURCE_ROOT"; }; | ||||
0473C3BCB7A43F710B8EE36C = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_MACAddress.h"; path = "../../../../modules/juce_core/network/juce_MACAddress.h"; sourceTree = "SOURCE_ROOT"; }; | 0473C3BCB7A43F710B8EE36C = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_MACAddress.h"; path = "../../../../modules/juce_core/network/juce_MACAddress.h"; sourceTree = "SOURCE_ROOT"; }; | ||||
04960EE443D073F4CB7121FB = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ToolbarItemFactory.h"; path = "../../../../modules/juce_gui_basics/widgets/juce_ToolbarItemFactory.h"; sourceTree = "SOURCE_ROOT"; }; | 04960EE443D073F4CB7121FB = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ToolbarItemFactory.h"; path = "../../../../modules/juce_gui_basics/widgets/juce_ToolbarItemFactory.h"; sourceTree = "SOURCE_ROOT"; }; | ||||
049A726AFEC564314777C076 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_ProjectExport_AndroidBase.h"; path = "../../Source/Project Saving/jucer_ProjectExport_AndroidBase.h"; sourceTree = "SOURCE_ROOT"; }; | |||||
04C1267B2BD11A6ECCCA654C = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_ComponentBooleanProperty.h"; path = "../../Source/ComponentEditor/properties/jucer_ComponentBooleanProperty.h"; sourceTree = "SOURCE_ROOT"; }; | 04C1267B2BD11A6ECCCA654C = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_ComponentBooleanProperty.h"; path = "../../Source/ComponentEditor/properties/jucer_ComponentBooleanProperty.h"; sourceTree = "SOURCE_ROOT"; }; | ||||
04F31392CA58A2A434CFF7FA = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_LowLevelGraphicsPostScriptRenderer.cpp"; path = "../../../../modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp"; sourceTree = "SOURCE_ROOT"; }; | 04F31392CA58A2A434CFF7FA = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_LowLevelGraphicsPostScriptRenderer.cpp"; path = "../../../../modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp"; sourceTree = "SOURCE_ROOT"; }; | ||||
04F670E070D31FB66C7DCAEB = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_LocalisedStrings.cpp"; path = "../../../../modules/juce_core/text/juce_LocalisedStrings.cpp"; sourceTree = "SOURCE_ROOT"; }; | 04F670E070D31FB66C7DCAEB = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_LocalisedStrings.cpp"; path = "../../../../modules/juce_core/text/juce_LocalisedStrings.cpp"; sourceTree = "SOURCE_ROOT"; }; | ||||
@@ -346,6 +347,7 @@ | |||||
4D698BF12BCD6B0896BCDF17 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_AutoUpdater.h"; path = "../../Source/Application/jucer_AutoUpdater.h"; sourceTree = "SOURCE_ROOT"; }; | 4D698BF12BCD6B0896BCDF17 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_AutoUpdater.h"; path = "../../Source/Application/jucer_AutoUpdater.h"; sourceTree = "SOURCE_ROOT"; }; | ||||
4D6F99ED00A4D8683AF313B2 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ColourGradient.h"; path = "../../../../modules/juce_graphics/colour/juce_ColourGradient.h"; sourceTree = "SOURCE_ROOT"; }; | 4D6F99ED00A4D8683AF313B2 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ColourGradient.h"; path = "../../../../modules/juce_graphics/colour/juce_ColourGradient.h"; sourceTree = "SOURCE_ROOT"; }; | ||||
4D7F53313945ED27A7D16B80 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_Result.h"; path = "../../../../modules/juce_core/misc/juce_Result.h"; sourceTree = "SOURCE_ROOT"; }; | 4D7F53313945ED27A7D16B80 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_Result.h"; path = "../../../../modules/juce_core/misc/juce_Result.h"; sourceTree = "SOURCE_ROOT"; }; | ||||
4DB834787369EBFCFF560F46 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_ProjectExport_AndroidAnt.h"; path = "../../Source/Project Saving/jucer_ProjectExport_AndroidAnt.h"; sourceTree = "SOURCE_ROOT"; }; | |||||
4E191CDCE7565DB726CF7065 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "jucer_ComponentOverlayComponent.cpp"; path = "../../Source/ComponentEditor/ui/jucer_ComponentOverlayComponent.cpp"; sourceTree = "SOURCE_ROOT"; }; | 4E191CDCE7565DB726CF7065 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "jucer_ComponentOverlayComponent.cpp"; path = "../../Source/ComponentEditor/ui/jucer_ComponentOverlayComponent.cpp"; sourceTree = "SOURCE_ROOT"; }; | ||||
4E259F36C28F1ACDAB4CC73F = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ModalComponentManager.h"; path = "../../../../modules/juce_gui_basics/components/juce_ModalComponentManager.h"; sourceTree = "SOURCE_ROOT"; }; | 4E259F36C28F1ACDAB4CC73F = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ModalComponentManager.h"; path = "../../../../modules/juce_gui_basics/components/juce_ModalComponentManager.h"; sourceTree = "SOURCE_ROOT"; }; | ||||
4E3AE065222C8C87691DF2DE = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_KeyListener.h"; path = "../../../../modules/juce_gui_basics/keyboard/juce_KeyListener.h"; sourceTree = "SOURCE_ROOT"; }; | 4E3AE065222C8C87691DF2DE = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_KeyListener.h"; path = "../../../../modules/juce_gui_basics/keyboard/juce_KeyListener.h"; sourceTree = "SOURCE_ROOT"; }; | ||||
@@ -462,7 +464,6 @@ | |||||
6DFAF945FC3A7D0689C5CEC8 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ArrayAllocationBase.h"; path = "../../../../modules/juce_core/containers/juce_ArrayAllocationBase.h"; sourceTree = "SOURCE_ROOT"; }; | 6DFAF945FC3A7D0689C5CEC8 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ArrayAllocationBase.h"; path = "../../../../modules/juce_core/containers/juce_ArrayAllocationBase.h"; sourceTree = "SOURCE_ROOT"; }; | ||||
6DFDC749FF36D8C27C997B3E = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_Button.cpp"; path = "../../../../modules/juce_gui_basics/buttons/juce_Button.cpp"; sourceTree = "SOURCE_ROOT"; }; | 6DFDC749FF36D8C27C997B3E = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_Button.cpp"; path = "../../../../modules/juce_gui_basics/buttons/juce_Button.cpp"; sourceTree = "SOURCE_ROOT"; }; | ||||
6E6140969908E7619F858740 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_CommonHeaders.h"; path = "../../Source/Application/jucer_CommonHeaders.h"; sourceTree = "SOURCE_ROOT"; }; | 6E6140969908E7619F858740 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_CommonHeaders.h"; path = "../../Source/Application/jucer_CommonHeaders.h"; sourceTree = "SOURCE_ROOT"; }; | ||||
6E7353DFEA8825B515049ABB = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_ProjectExport_Android.h"; path = "../../Source/Project Saving/jucer_ProjectExport_Android.h"; sourceTree = "SOURCE_ROOT"; }; | |||||
6E815592344CAA798C9848BF = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_WeakReference.h"; path = "../../../../modules/juce_core/memory/juce_WeakReference.h"; sourceTree = "SOURCE_ROOT"; }; | 6E815592344CAA798C9848BF = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_WeakReference.h"; path = "../../../../modules/juce_core/memory/juce_WeakReference.h"; sourceTree = "SOURCE_ROOT"; }; | ||||
6E8B46E33BF7A0DD930A5100 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_FileTreeComponent.cpp"; path = "../../../../modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp"; sourceTree = "SOURCE_ROOT"; }; | 6E8B46E33BF7A0DD930A5100 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_FileTreeComponent.cpp"; path = "../../../../modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp"; sourceTree = "SOURCE_ROOT"; }; | ||||
6EE1847181635ED3C0838A4B = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_LowLevelGraphicsPostScriptRenderer.h"; path = "../../../../modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h"; sourceTree = "SOURCE_ROOT"; }; | 6EE1847181635ED3C0838A4B = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_LowLevelGraphicsPostScriptRenderer.h"; path = "../../../../modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h"; sourceTree = "SOURCE_ROOT"; }; | ||||
@@ -1086,8 +1087,9 @@ | |||||
66B49F08C5EC3E4974825FF8, | 66B49F08C5EC3E4974825FF8, | ||||
5432B7B9B2CF2EAEC8B66D5C, ); name = ComponentEditor; sourceTree = "<group>"; }; | 5432B7B9B2CF2EAEC8B66D5C, ); name = ComponentEditor; sourceTree = "<group>"; }; | ||||
E345840128627D533DF908AC = {isa = PBXGroup; children = ( | E345840128627D533DF908AC = {isa = PBXGroup; children = ( | ||||
6E7353DFEA8825B515049ABB, | |||||
049A726AFEC564314777C076, | |||||
16385D79A30C6E06EFB46B79, | 16385D79A30C6E06EFB46B79, | ||||
4DB834787369EBFCFF560F46, | |||||
DBE0CDE1B017190ABBFF557C, | DBE0CDE1B017190ABBFF557C, | ||||
05076CDF1511A5F8A8E18F1D, | 05076CDF1511A5F8A8E18F1D, | ||||
C8A229ACD244F402C57286CD, | C8A229ACD244F402C57286CD, | ||||
@@ -1141,8 +1141,9 @@ | |||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_ObjectTypes.h"/> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_ObjectTypes.h"/> | ||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_PaintRoutine.h"/> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_PaintRoutine.h"/> | ||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"/> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Android.h"/> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidBase.h"/> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidAnt.h"/> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Make.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Make.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_MSVC.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_MSVC.h"/> | ||||
@@ -1590,12 +1590,15 @@ | |||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"> | ||||
<Filter>The Introjucer\ComponentEditor</Filter> | <Filter>The Introjucer\ComponentEditor</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Android.h"> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidBase.h"> | |||||
<Filter>The Introjucer\Project Saving</Filter> | <Filter>The Introjucer\Project Saving</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"> | ||||
<Filter>The Introjucer\Project Saving</Filter> | <Filter>The Introjucer\Project Saving</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidAnt.h"> | |||||
<Filter>The Introjucer\Project Saving</Filter> | |||||
</ClInclude> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"> | ||||
<Filter>The Introjucer\Project Saving</Filter> | <Filter>The Introjucer\Project Saving</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
@@ -1147,8 +1147,9 @@ | |||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_ObjectTypes.h"/> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_ObjectTypes.h"/> | ||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_PaintRoutine.h"/> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_PaintRoutine.h"/> | ||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"/> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Android.h"/> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidBase.h"/> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidAnt.h"/> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Make.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Make.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_MSVC.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_MSVC.h"/> | ||||
@@ -1590,12 +1590,15 @@ | |||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"> | ||||
<Filter>The Introjucer\ComponentEditor</Filter> | <Filter>The Introjucer\ComponentEditor</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Android.h"> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidBase.h"> | |||||
<Filter>The Introjucer\Project Saving</Filter> | <Filter>The Introjucer\Project Saving</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"> | ||||
<Filter>The Introjucer\Project Saving</Filter> | <Filter>The Introjucer\Project Saving</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidAnt.h"> | |||||
<Filter>The Introjucer\Project Saving</Filter> | |||||
</ClInclude> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"> | ||||
<Filter>The Introjucer\Project Saving</Filter> | <Filter>The Introjucer\Project Saving</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
@@ -1147,8 +1147,9 @@ | |||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_ObjectTypes.h"/> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_ObjectTypes.h"/> | ||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_PaintRoutine.h"/> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_PaintRoutine.h"/> | ||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"/> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Android.h"/> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidBase.h"/> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidAnt.h"/> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Make.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Make.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_MSVC.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_MSVC.h"/> | ||||
@@ -1590,12 +1590,15 @@ | |||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"> | ||||
<Filter>The Introjucer\ComponentEditor</Filter> | <Filter>The Introjucer\ComponentEditor</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Android.h"> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidBase.h"> | |||||
<Filter>The Introjucer\Project Saving</Filter> | <Filter>The Introjucer\Project Saving</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"> | ||||
<Filter>The Introjucer\Project Saving</Filter> | <Filter>The Introjucer\Project Saving</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidAnt.h"> | |||||
<Filter>The Introjucer\Project Saving</Filter> | |||||
</ClInclude> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"> | ||||
<Filter>The Introjucer\Project Saving</Filter> | <Filter>The Introjucer\Project Saving</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
@@ -1147,8 +1147,9 @@ | |||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_ObjectTypes.h"/> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_ObjectTypes.h"/> | ||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_PaintRoutine.h"/> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_PaintRoutine.h"/> | ||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"/> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Android.h"/> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidBase.h"/> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidAnt.h"/> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Make.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Make.h"/> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_MSVC.h"/> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_MSVC.h"/> | ||||
@@ -1590,12 +1590,15 @@ | |||||
<ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"> | <ClInclude Include="..\..\Source\ComponentEditor\jucer_UtilityFunctions.h"> | ||||
<Filter>The Introjucer\ComponentEditor</Filter> | <Filter>The Introjucer\ComponentEditor</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_Android.h"> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidBase.h"> | |||||
<Filter>The Introjucer\Project Saving</Filter> | <Filter>The Introjucer\Project Saving</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidStudio.h"> | ||||
<Filter>The Introjucer\Project Saving</Filter> | <Filter>The Introjucer\Project Saving</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_AndroidAnt.h"> | |||||
<Filter>The Introjucer\Project Saving</Filter> | |||||
</ClInclude> | |||||
<ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"> | <ClInclude Include="..\..\Source\Project Saving\jucer_ProjectExport_CodeBlocks.h"> | ||||
<Filter>The Introjucer\Project Saving</Filter> | <Filter>The Introjucer\Project Saving</Filter> | ||||
</ClInclude> | </ClInclude> | ||||
@@ -364,10 +364,12 @@ | |||||
file="Source/ComponentEditor/jucer_UtilityFunctions.h"/> | file="Source/ComponentEditor/jucer_UtilityFunctions.h"/> | ||||
</GROUP> | </GROUP> | ||||
<GROUP id="{579C9644-D5C2-8469-9439-F91C81337531}" name="Project Saving"> | <GROUP id="{579C9644-D5C2-8469-9439-F91C81337531}" name="Project Saving"> | ||||
<FILE id="TtXohM" name="jucer_ProjectExport_Android.h" compile="0" | |||||
resource="0" file="Source/Project Saving/jucer_ProjectExport_Android.h"/> | |||||
<FILE id="TtXohM" name="jucer_ProjectExport_AndroidBase.h" compile="0" | |||||
resource="0" file="Source/Project Saving/jucer_ProjectExport_AndroidBase.h"/> | |||||
<FILE id="AhU3qX" name="jucer_ProjectExport_AndroidStudio.h" compile="0" | <FILE id="AhU3qX" name="jucer_ProjectExport_AndroidStudio.h" compile="0" | ||||
resource="0" file="Source/Project Saving/jucer_ProjectExport_AndroidStudio.h"/> | resource="0" file="Source/Project Saving/jucer_ProjectExport_AndroidStudio.h"/> | ||||
<FILE id="dfcn8d" name="jucer_ProjectExport_AndroidAnt.h" compile="0" | |||||
resource="0" file="Source/Project Saving/jucer_ProjectExport_AndroidAnt.h"/> | |||||
<FILE id="EkBkj1" name="jucer_ProjectExport_CodeBlocks.h" compile="0" | <FILE id="EkBkj1" name="jucer_ProjectExport_CodeBlocks.h" compile="0" | ||||
resource="0" file="Source/Project Saving/jucer_ProjectExport_CodeBlocks.h"/> | resource="0" file="Source/Project Saving/jucer_ProjectExport_CodeBlocks.h"/> | ||||
<FILE id="mVXrLi" name="jucer_ProjectExport_Make.h" compile="0" resource="0" | <FILE id="mVXrLi" name="jucer_ProjectExport_Make.h" compile="0" resource="0" | ||||
@@ -0,0 +1,459 @@ | |||||
/* | |||||
============================================================================== | |||||
This file is part of the JUCE library. | |||||
Copyright (c) 2016 - ROLI 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 AndroidAntProjectExporter : public AndroidProjectExporterBase | |||||
{ | |||||
public: | |||||
//============================================================================== | |||||
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; } | |||||
bool isAndroidStudio() override { return false; } | |||||
bool isAndroidAnt() override { return true; } | |||||
//============================================================================== | |||||
static const char* getName() { return "Android Ant Project"; } | |||||
static const char* getValueTreeTypeName() { return "ANDROID"; } | |||||
//============================================================================== | |||||
Value getNDKToolchainVersionValue() { return getSetting (Ids::toolset); } | |||||
String getNDKToolchainVersionString() const { return settings [Ids::toolset]; } | |||||
Value getStaticLibrariesValue() { return getSetting (Ids::androidStaticLibraries); } | |||||
String getStaticLibrariesString() const { return settings [Ids::androidStaticLibraries]; } | |||||
Value getSharedLibrariesValue() { return getSetting (Ids::androidSharedLibraries); } | |||||
String getSharedLibrariesString() const { return settings [Ids::androidSharedLibraries]; } | |||||
//============================================================================== | |||||
static AndroidAntProjectExporter* createForSettings (Project& project, const ValueTree& settings) | |||||
{ | |||||
if (settings.hasType (getValueTreeTypeName())) | |||||
return new AndroidAntProjectExporter (project, settings); | |||||
return nullptr; | |||||
} | |||||
//============================================================================== | |||||
AndroidAntProjectExporter (Project& p, const ValueTree& t) | |||||
: AndroidProjectExporterBase (p, t) | |||||
{ | |||||
name = getName(); | |||||
if (getTargetLocationString().isEmpty()) | |||||
getTargetLocationValue() = getDefaultBuildsRootFolder() + "Android"; | |||||
} | |||||
//============================================================================== | |||||
void createToolchainExporterProperties (PropertyListBuilder& props) override | |||||
{ | |||||
props.add (new TextPropertyComponent (getNDKToolchainVersionValue(), "NDK Toolchain version", 32, false), | |||||
"The variable NDK_TOOLCHAIN_VERSION in Application.mk - leave blank for a default value"); | |||||
} | |||||
void createLibraryModuleExporterProperties (PropertyListBuilder& props) override | |||||
{ | |||||
props.add (new TextPropertyComponent (getStaticLibrariesValue(), "Import static library modules", 8192, true), | |||||
"Comma or whitespace delimited list of static libraries (.a) defined in NDK_MODULE_PATH."); | |||||
props.add (new TextPropertyComponent (getSharedLibrariesValue(), "Import shared library modules", 8192, true), | |||||
"Comma or whitespace delimited list of shared libraries (.so) defined in NDK_MODULE_PATH."); | |||||
} | |||||
//============================================================================== | |||||
void create (const OwnedArray<LibraryModule>& modules) const override | |||||
{ | |||||
AndroidProjectExporterBase::create (modules); | |||||
const File target (getTargetFolder()); | |||||
const File jniFolder (target.getChildFile ("jni")); | |||||
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")); | |||||
writeIcons (target.getChildFile ("res")); | |||||
} | |||||
//============================================================================== | |||||
class AndroidBuildConfiguration : public BuildConfiguration | |||||
{ | |||||
public: | |||||
AndroidBuildConfiguration (Project& p, const ValueTree& settings, const ProjectExporter& e) | |||||
: BuildConfiguration (p, settings, e) | |||||
{ | |||||
if (getArchitectures().isEmpty()) | |||||
{ | |||||
if (isDebug()) | |||||
getArchitecturesValue() = "armeabi x86"; | |||||
else | |||||
getArchitecturesValue() = "armeabi armeabi-v7a x86"; | |||||
} | |||||
} | |||||
Value getArchitecturesValue() { return getValue (Ids::androidArchitectures); } | |||||
String getArchitectures() const { return config [Ids::androidArchitectures]; } | |||||
var getDefaultOptimisationLevel() const override { return var ((int) (isDebug() ? gccO0 : gccO3)); } | |||||
void createConfigProperties (PropertyListBuilder& props) override | |||||
{ | |||||
addGCCOptimisationProperty (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 override | |||||
{ | |||||
return new AndroidBuildConfiguration (project, v, *this); | |||||
} | |||||
private: | |||||
//============================================================================== | |||||
String getToolchainVersion() const | |||||
{ | |||||
String v (getNDKToolchainVersionString()); | |||||
return v.isNotEmpty() ? v : "4.8"; | |||||
} | |||||
//============================================================================== | |||||
String getCppFlags() const | |||||
{ | |||||
String flags ("-fsigned-char -fexceptions -frtti"); | |||||
if (! getNDKToolchainVersionString().startsWithIgnoreCase ("clang")) | |||||
flags << " -Wno-psabi"; | |||||
return flags; | |||||
} | |||||
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 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<AndroidBuildConfiguration> (true) << newLine | |||||
<< "else" << newLine | |||||
<< " APP_ABI := " << getABIs<AndroidBuildConfiguration> (false) << newLine | |||||
<< "endif" << newLine; | |||||
overwriteFileIfDifferentOrThrow (file, mo); | |||||
} | |||||
struct ShouldFileBeCompiledPredicate | |||||
{ | |||||
bool operator() (const Project::Item& projectItem) const { return projectItem.shouldBeCompiled(); } | |||||
}; | |||||
void writeAndroidMk (const File& file) const | |||||
{ | |||||
Array<RelativePath> files; | |||||
for (int i = 0; i < getAllGroups().size(); ++i) | |||||
findAllProjectItemsWithPredicate (getAllGroups().getReference(i), files, ShouldFileBeCompiledPredicate()); | |||||
MemoryOutputStream mo; | |||||
writeAndroidMk (mo, files); | |||||
overwriteFileIfDifferentOrThrow (file, mo); | |||||
} | |||||
void writeAndroidMkVariableList (OutputStream& out, const String& variableName, const String& settingsValue) const | |||||
{ | |||||
const StringArray separatedItems (getCommaOrWhitespaceSeparatedItems (settingsValue)); | |||||
if (separatedItems.size() > 0) | |||||
out << newLine << variableName << " := " << separatedItems.joinIntoString (" ") << newLine; | |||||
} | |||||
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; | |||||
writeAndroidMkVariableList (out, "LOCAL_STATIC_LIBRARIES", getStaticLibrariesString()); | |||||
writeAndroidMkVariableList (out, "LOCAL_SHARED_LIBRARIES", getSharedLibrariesString()); | |||||
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; | |||||
StringArray importModules (getCommaOrWhitespaceSeparatedItems (getStaticLibrariesString())); | |||||
importModules.addArray (getCommaOrWhitespaceSeparatedItems (getSharedLibrariesString())); | |||||
for (int i = 0; i < importModules.size(); ++i) | |||||
out << "$(call import-module," << importModules[i] << ")" << 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 -landroid -lEGL" + getExternalLibraryFlags (config) | |||||
+ " " + replacePreprocessorTokens (config, getExtraLinkerFlagsString()); | |||||
} | |||||
String createIncludePathFlags (const BuildConfiguration& config) const | |||||
{ | |||||
String flags; | |||||
StringArray searchPaths (extraSearchPaths); | |||||
searchPaths.addArray (config.getHeaderSearchPaths()); | |||||
searchPaths = getCleanedStringArray (searchPaths); | |||||
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(); | |||||
flags << " -std=gnu++11"; | |||||
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=4"); | |||||
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"); | |||||
} | |||||
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 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 (AndroidAntProjectExporter) | |||||
}; |
@@ -25,8 +25,19 @@ | |||||
class AndroidProjectExporterBase : public ProjectExporter | class AndroidProjectExporterBase : public ProjectExporter | ||||
{ | { | ||||
public: | public: | ||||
//========================================================================== | |||||
AndroidProjectExporterBase (Project& p, const ValueTree& t) | AndroidProjectExporterBase (Project& p, const ValueTree& t) | ||||
: ProjectExporter (p, t) | : ProjectExporter (p, t) | ||||
{ | |||||
setEmptyPropertiesToDefaultValues(); | |||||
} | |||||
//========================================================================== | |||||
virtual bool isAndroidStudio() = 0; | |||||
virtual bool isAndroidAnt() = 0; | |||||
//========================================================================== | |||||
void setEmptyPropertiesToDefaultValues() | |||||
{ | { | ||||
if (getVersionCodeString().isEmpty()) | if (getVersionCodeString().isEmpty()) | ||||
getVersionCodeValue() = 1; | getVersionCodeValue() = 1; | ||||
@@ -47,7 +58,6 @@ public: | |||||
if (getKeyStorePassValue().getValue().isVoid()) getKeyStorePassValue() = "android"; | if (getKeyStorePassValue().getValue().isVoid()) getKeyStorePassValue() = "android"; | ||||
if (getKeyAliasValue().getValue().isVoid()) getKeyAliasValue() = "androiddebugkey"; | if (getKeyAliasValue().getValue().isVoid()) getKeyAliasValue() = "androiddebugkey"; | ||||
if (getKeyAliasPassValue().getValue().isVoid()) getKeyAliasPassValue() = "android"; | if (getKeyAliasPassValue().getValue().isVoid()) getKeyAliasPassValue() = "android"; | ||||
if (getCPP11EnabledValue().getValue().isVoid()) getCPP11EnabledValue() = true; | |||||
initialiseDependencyPathValues(); | initialiseDependencyPathValues(); | ||||
@@ -55,12 +65,7 @@ public: | |||||
getScreenOrientationValue() = "unspecified"; | getScreenOrientationValue() = "unspecified"; | ||||
} | } | ||||
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 create (const OwnedArray<LibraryModule>& modules) const override | void create (const OwnedArray<LibraryModule>& modules) const override | ||||
{ | { | ||||
const String package (getActivityClassPackage()); | const String package (getActivityClassPackage()); | ||||
@@ -70,9 +75,78 @@ public: | |||||
copyActivityJavaFiles (modules, target, package); | copyActivityJavaFiles (modules, target, package); | ||||
} | } | ||||
//========================================================================== | |||||
// base properties | |||||
Value getScreenOrientationValue() { return getSetting (Ids::androidScreenOrientation); } | |||||
String getScreenOrientationString() const { return settings [Ids::androidScreenOrientation]; } | |||||
Value getActivityClassPathValue() { return getSetting (Ids::androidActivityClass); } | |||||
String getActivityClassPath() const { return settings [Ids::androidActivityClass]; } | |||||
Value getActivitySubClassPathValue() { return getSetting (Ids::androidActivitySubClassName); } | |||||
String getActivitySubClassPath() const { return settings [Ids::androidActivitySubClassName]; } | |||||
Value getVersionCodeValue() { return getSetting (Ids::androidVersionCode); } | |||||
String getVersionCodeString() const { return settings [Ids::androidVersionCode]; } | |||||
Value getSDKPathValue() { return sdkPath; } | |||||
String getSDKPathString() const { return sdkPath.toString(); } | |||||
Value getNDKPathValue() { return ndkPath; } | |||||
String getNDKPathString() const { return ndkPath.toString(); } | |||||
Value getMinimumSDKVersionValue() { return getSetting (Ids::androidMinimumSDK); } | |||||
String getMinimumSDKVersionString() const { return settings [Ids::androidMinimumSDK]; } | |||||
// manifest properties | |||||
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 getBluetoothPermissionsValue() { return getSetting(Ids::androidBluetoothNeeded); } | |||||
bool getBluetoothPermissions() const { return settings[Ids::androidBluetoothNeeded]; } | |||||
Value getOtherPermissionsValue() { return getSetting (Ids::androidOtherPermissions); } | |||||
String getOtherPermissions() const { return settings [Ids::androidOtherPermissions]; } | |||||
// code signing properties | |||||
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]; } | |||||
// other properties | |||||
Value getThemeValue() { return getSetting (Ids::androidTheme); } | |||||
String getThemeString() const { return settings [Ids::androidTheme]; } | |||||
//========================================================================== | |||||
void createExporterProperties (PropertyListBuilder& props) override | void createExporterProperties (PropertyListBuilder& props) override | ||||
{ | { | ||||
addScreenOrientationProperty (props); | |||||
createBaseExporterProperties (props); | |||||
createToolchainExporterProperties (props); | |||||
createManifestExporterProperties (props); | |||||
createLibraryModuleExporterProperties (props); | |||||
createCodeSigningExporterProperties (props); | |||||
createOtherExporterProperties (props); | |||||
} | |||||
//========================================================================== | |||||
enum ScreenOrientation | |||||
{ | |||||
unspecified = 1, | |||||
portrait = 2, | |||||
landscape = 3 | |||||
}; | |||||
//============================================================================== | |||||
void createBaseExporterProperties (PropertyListBuilder& props) | |||||
{ | |||||
static const char* orientations[] = { "Portrait and Landscape", "Portrait", "Landscape", nullptr }; | |||||
static const char* orientationValues[] = { "unspecified", "portrait", "landscape", nullptr }; | |||||
props.add (new ChoicePropertyComponent (getScreenOrientationValue(), "Screen orientation", StringArray (orientations), Array<var> (orientationValues)), | |||||
"The screen orientation that this app should use"); | |||||
props.add (new TextPropertyComponent (getActivityClassPathValue(), "Android Activity class name", 256, false), | props.add (new TextPropertyComponent (getActivityClassPathValue(), "Android Activity class name", 256, false), | ||||
"The full java class name to use for the app's Activity class."); | "The full java class name to use for the app's Activity class."); | ||||
@@ -92,13 +166,14 @@ public: | |||||
props.add (new TextPropertyComponent (getMinimumSDKVersionValue(), "Minimum SDK version", 32, false), | props.add (new TextPropertyComponent (getMinimumSDKVersionValue(), "Minimum SDK version", 32, false), | ||||
"The number of the minimum version of the Android SDK that the app requires"); | "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."); | |||||
//========================================================================== | |||||
virtual void createToolchainExporterProperties (PropertyListBuilder& props) = 0; // different for ant and Android Studio | |||||
//========================================================================== | |||||
void createManifestExporterProperties (PropertyListBuilder& props) | |||||
{ | |||||
props.add (new BooleanPropertyComponent (getInternetNeededValue(), "Internet Access", "Specify internet access permission in the manifest"), | 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."); | "If enabled, this will set the android.permission.INTERNET flag in the manifest."); | ||||
@@ -110,100 +185,34 @@ public: | |||||
props.add (new TextPropertyComponent (getOtherPermissionsValue(), "Custom permissions", 2048, false), | props.add (new TextPropertyComponent (getOtherPermissionsValue(), "Custom permissions", 2048, false), | ||||
"A space-separated list of other permission flags that should be added to the manifest."); | "A space-separated list of other permission flags that should be added to the manifest."); | ||||
} | |||||
props.add (new TextPropertyComponent (getStaticLibrariesValue(), "Import static library modules", 8192, true), | |||||
"Comma or whitespace delimited list of static libraries (.a) defined in NDK_MODULE_PATH."); | |||||
props.add (new TextPropertyComponent (getSharedLibrariesValue(), "Import shared library modules", 8192, true), | |||||
"Comma or whitespace delimited list of shared libraries (.so) defined in NDK_MODULE_PATH."); | |||||
props.add (new TextPropertyComponent (getThemeValue(), "Android Theme", 256, false), | |||||
"E.g. @android:style/Theme.NoTitleBar or leave blank for default"); | |||||
//========================================================================== | |||||
virtual void createLibraryModuleExporterProperties (PropertyListBuilder& props) = 0; // different for ant and Android Studio | |||||
//========================================================================== | |||||
void createCodeSigningExporterProperties (PropertyListBuilder& props) | |||||
{ | |||||
props.add (new TextPropertyComponent (getKeyStoreValue(), "Key Signing: key.store", 2048, false), | props.add (new TextPropertyComponent (getKeyStoreValue(), "Key Signing: key.store", 2048, false), | ||||
"The key.store value, used when signing the package."); | "The key.store value, used when signing the package."); | ||||
props.add (new TextPropertyComponent (getKeyStorePassValue(), "Key Signing: key.store.password", 2048, false), | props.add (new TextPropertyComponent (getKeyStorePassValue(), "Key Signing: key.store.password", 2048, false), | ||||
"The key.store password, used when signing the package."); | "The key.store password, used when signing the package."); | ||||
props.add (new TextPropertyComponent (getKeyAliasValue(), "Key Signing: key.alias", 2048, false), | props.add (new TextPropertyComponent (getKeyAliasValue(), "Key Signing: key.alias", 2048, false), | ||||
"The key.alias value, used when signing the package."); | "The key.alias value, used when signing the package."); | ||||
props.add (new TextPropertyComponent (getKeyAliasPassValue(), "Key Signing: key.alias.password", 2048, false), | props.add (new TextPropertyComponent (getKeyAliasPassValue(), "Key Signing: key.alias.password", 2048, false), | ||||
"The key.alias password, used when signing the package."); | "The key.alias password, used when signing the package."); | ||||
} | } | ||||
enum ScreenOrientation | |||||
//========================================================================== | |||||
void createOtherExporterProperties (PropertyListBuilder& props) | |||||
{ | { | ||||
unspecified = 1, | |||||
portrait = 2, | |||||
landscape = 3 | |||||
}; | |||||
void addScreenOrientationProperty (PropertyListBuilder& props) | |||||
{ | |||||
static const char* orientations[] = { "Unspecified", | |||||
"Portrait", | |||||
"Landscape", | |||||
nullptr }; | |||||
static const char* orientationValues[] = { "unspecified", | |||||
"portrait", | |||||
"landscape", | |||||
0 }; | |||||
props.add (new ChoicePropertyComponent (getScreenOrientationValue(), | |||||
"Screen orientation", | |||||
StringArray (orientations), | |||||
Array<var> (orientationValues)), | |||||
"The screen orientation that this app should use"); | |||||
props.add (new TextPropertyComponent (getThemeValue(), "Android Theme", 256, false), | |||||
"E.g. @android:style/Theme.NoTitleBar or leave blank for default"); | |||||
} | } | ||||
Value getActivityClassPathValue() { return getSetting (Ids::androidActivityClass); } | |||||
String getActivityClassPath() const { return settings [Ids::androidActivityClass]; } | |||||
Value getActivitySubClassPathValue() { return getSetting (Ids::androidActivitySubClassName); } | |||||
String getActivitySubClassPath() const { return settings [Ids::androidActivitySubClassName]; } | |||||
Value getVersionCodeValue() { return getSetting (Ids::androidVersionCode); } | |||||
String getVersionCodeString() const { return settings [Ids::androidVersionCode]; } | |||||
Value getSDKPathValue() { return sdkPath; } | |||||
String getSDKPathString() const { return sdkPath.toString(); } | |||||
Value getNDKPathValue() { return ndkPath; } | |||||
String getNDKPathString() const { return ndkPath.toString(); } | |||||
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 getBluetoothPermissionsValue() { return getSetting(Ids::androidBluetoothNeeded); } | |||||
bool getBluetoothPermissions() const { return settings[Ids::androidBluetoothNeeded]; } | |||||
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 getThemeValue() { return getSetting (Ids::androidTheme); } | |||||
String getThemeString() const { return settings [Ids::androidTheme]; } | |||||
Value getStaticLibrariesValue() { return getSetting (Ids::androidStaticLibraries); } | |||||
String getStaticLibrariesString() const { return settings [Ids::androidStaticLibraries]; } | |||||
Value getSharedLibrariesValue() { return getSetting (Ids::androidSharedLibraries); } | |||||
String getSharedLibrariesString() const { return settings [Ids::androidSharedLibraries]; } | |||||
Value getCPP11EnabledValue() { return getSetting (Ids::androidCpp11); } | |||||
bool isCPP11Enabled() const { return settings [Ids::androidCpp11]; } | |||||
Value getScreenOrientationValue() { return getSetting (Ids::androidScreenOrientation); } | |||||
String getScreenOrientationString() const { return settings [Ids::androidScreenOrientation]; } | |||||
//============================================================================== | //============================================================================== | ||||
String createDefaultClassName() const | String createDefaultClassName() const | ||||
{ | { | ||||
@@ -309,15 +318,6 @@ public: | |||||
} | } | ||||
} | } | ||||
String getAppPlatform() const | |||||
{ | |||||
int ndkVersion = getMinimumSDKVersionString().getIntValue(); | |||||
if (ndkVersion == 9) | |||||
ndkVersion = 10; // (doesn't seem to be a version '9') | |||||
return "android-" + String (ndkVersion); | |||||
} | |||||
String getActivityName() const | String getActivityName() const | ||||
{ | { | ||||
return getActivityClassPath().fromLastOccurrenceOf (".", false, false); | return getActivityClassPath().fromLastOccurrenceOf (".", false, false); | ||||
@@ -349,16 +349,6 @@ public: | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
String getCppFlags() const | |||||
{ | |||||
String flags ("-fsigned-char -fexceptions -frtti"); | |||||
if (! getNDKToolchainVersionString().startsWithIgnoreCase ("clang")) | |||||
flags << " -Wno-psabi"; | |||||
return flags; | |||||
} | |||||
StringArray getPermissionsRequired() const | StringArray getPermissionsRequired() const | ||||
{ | { | ||||
StringArray s; | StringArray s; | ||||
@@ -514,393 +504,3 @@ public: | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidProjectExporterBase) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidProjectExporterBase) | ||||
}; | }; | ||||
//============================================================================== | |||||
//============================================================================== | |||||
class AndroidAntProjectExporter : public AndroidProjectExporterBase | |||||
{ | |||||
public: | |||||
//============================================================================== | |||||
static const char* getName() { return "Android Ant Project"; } | |||||
static const char* getValueTreeTypeName() { return "ANDROID"; } | |||||
static AndroidAntProjectExporter* createForSettings (Project& project, const ValueTree& settings) | |||||
{ | |||||
if (settings.hasType (getValueTreeTypeName())) | |||||
return new AndroidAntProjectExporter (project, settings); | |||||
return nullptr; | |||||
} | |||||
//============================================================================== | |||||
AndroidAntProjectExporter (Project& p, const ValueTree& t) | |||||
: AndroidProjectExporterBase (p, t) | |||||
{ | |||||
name = getName(); | |||||
if (getTargetLocationString().isEmpty()) | |||||
getTargetLocationValue() = getDefaultBuildsRootFolder() + "Android"; | |||||
} | |||||
//============================================================================== | |||||
void createExporterProperties (PropertyListBuilder& props) override | |||||
{ | |||||
AndroidProjectExporterBase::createExporterProperties (props); | |||||
} | |||||
void create (const OwnedArray<LibraryModule>& modules) const override | |||||
{ | |||||
AndroidProjectExporterBase::create (modules); | |||||
const File target (getTargetFolder()); | |||||
const File jniFolder (target.getChildFile ("jni")); | |||||
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")); | |||||
writeIcons (target.getChildFile ("res")); | |||||
} | |||||
//============================================================================== | |||||
class AndroidBuildConfiguration : public BuildConfiguration | |||||
{ | |||||
public: | |||||
AndroidBuildConfiguration (Project& p, const ValueTree& settings, const ProjectExporter& e) | |||||
: BuildConfiguration (p, settings, e) | |||||
{ | |||||
if (getArchitectures().isEmpty()) | |||||
{ | |||||
if (isDebug()) | |||||
getArchitecturesValue() = "armeabi x86"; | |||||
else | |||||
getArchitecturesValue() = "armeabi armeabi-v7a x86"; | |||||
} | |||||
} | |||||
Value getArchitecturesValue() { return getValue (Ids::androidArchitectures); } | |||||
String getArchitectures() const { return config [Ids::androidArchitectures]; } | |||||
var getDefaultOptimisationLevel() const override { return var ((int) (isDebug() ? gccO0 : gccO3)); } | |||||
void createConfigProperties (PropertyListBuilder& props) override | |||||
{ | |||||
addGCCOptimisationProperty (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 override | |||||
{ | |||||
return new AndroidBuildConfiguration (project, v, *this); | |||||
} | |||||
private: | |||||
//============================================================================== | |||||
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<AndroidBuildConfiguration> (true) << newLine | |||||
<< "else" << newLine | |||||
<< " APP_ABI := " << getABIs<AndroidBuildConfiguration> (false) << newLine | |||||
<< "endif" << newLine; | |||||
overwriteFileIfDifferentOrThrow (file, mo); | |||||
} | |||||
struct ShouldFileBeCompiledPredicate | |||||
{ | |||||
bool operator() (const Project::Item& projectItem) const { return projectItem.shouldBeCompiled(); } | |||||
}; | |||||
void writeAndroidMk (const File& file) const | |||||
{ | |||||
Array<RelativePath> files; | |||||
for (int i = 0; i < getAllGroups().size(); ++i) | |||||
findAllProjectItemsWithPredicate (getAllGroups().getReference(i), files, ShouldFileBeCompiledPredicate()); | |||||
MemoryOutputStream mo; | |||||
writeAndroidMk (mo, files); | |||||
overwriteFileIfDifferentOrThrow (file, mo); | |||||
} | |||||
void writeAndroidMkVariableList (OutputStream& out, const String& variableName, const String& settingsValue) const | |||||
{ | |||||
const StringArray separatedItems (getCommaOrWhitespaceSeparatedItems (settingsValue)); | |||||
if (separatedItems.size() > 0) | |||||
out << newLine << variableName << " := " << separatedItems.joinIntoString (" ") << newLine; | |||||
} | |||||
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; | |||||
writeAndroidMkVariableList (out, "LOCAL_STATIC_LIBRARIES", getStaticLibrariesString()); | |||||
writeAndroidMkVariableList (out, "LOCAL_SHARED_LIBRARIES", getSharedLibrariesString()); | |||||
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; | |||||
StringArray importModules (getCommaOrWhitespaceSeparatedItems (getStaticLibrariesString())); | |||||
importModules.addArray (getCommaOrWhitespaceSeparatedItems (getSharedLibrariesString())); | |||||
for (int i = 0; i < importModules.size(); ++i) | |||||
out << "$(call import-module," << importModules[i] << ")" << 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 -landroid -lEGL" + getExternalLibraryFlags (config) | |||||
+ " " + replacePreprocessorTokens (config, getExtraLinkerFlagsString()); | |||||
} | |||||
String createIncludePathFlags (const BuildConfiguration& config) const | |||||
{ | |||||
String flags; | |||||
StringArray searchPaths (extraSearchPaths); | |||||
searchPaths.addArray (config.getHeaderSearchPaths()); | |||||
searchPaths = getCleanedStringArray (searchPaths); | |||||
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=4"); | |||||
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"); | |||||
} | |||||
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 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 (AndroidAntProjectExporter) | |||||
}; |
@@ -25,7 +25,13 @@ | |||||
class AndroidStudioProjectExporter : public AndroidProjectExporterBase | class AndroidStudioProjectExporter : public AndroidProjectExporterBase | ||||
{ | { | ||||
public: | public: | ||||
//============================================================================== | |||||
//========================================================================== | |||||
bool isAndroid() const override { return true; } | |||||
bool usesMMFiles() const override { return false; } | |||||
bool canCopeWithDuplicateFiles() override { return false; } | |||||
bool isAndroidStudio() override { return true; } | |||||
bool isAndroidAnt() override { return false; } | |||||
static const char* getName() { return "Android Studio"; } | static const char* getName() { return "Android Studio"; } | ||||
static const char* getValueTreeTypeName() { return "ANDROIDSTUDIO"; } | static const char* getValueTreeTypeName() { return "ANDROIDSTUDIO"; } | ||||
@@ -37,15 +43,69 @@ public: | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
//============================================================================== | |||||
Value getGradleVersionValue() { return getSetting (Ids::gradleVersion); } | |||||
String getGradleVersionString() const { return settings [Ids::gradleVersion]; } | |||||
Value getGradleWrapperVersionValue() { return getSetting (Ids::gradleWrapperVersion); } | |||||
String getGradleWrapperVersionString() const { return settings [Ids::gradleWrapperVersion]; } | |||||
Value getGradleToolchainValue() { return getSetting (Ids::gradleToolchain); } | |||||
String getGradleToolchainString() const { return settings [Ids::gradleToolchain]; } | |||||
Value getGradleToolchainVersionValue() { return getSetting (Ids::gradleToolchainVersion); } | |||||
String getGradleToolchainVersionString() const { return settings [Ids::gradleToolchainVersion]; } | |||||
//============================================================================== | //============================================================================== | ||||
AndroidStudioProjectExporter (Project& p, const ValueTree& t) | AndroidStudioProjectExporter (Project& p, const ValueTree& t) | ||||
: AndroidProjectExporterBase (p, t), | : AndroidProjectExporterBase (p, t), | ||||
androidStudioExecutable (findAndroidStudioExecutable()) | androidStudioExecutable (findAndroidStudioExecutable()) | ||||
{ | { | ||||
name = getName(); | name = getName(); | ||||
setEmptyPropertiesToDefaultValues(); | |||||
} | |||||
//============================================================================== | |||||
void setEmptyPropertiesToDefaultValues() | |||||
{ | |||||
if (getTargetLocationString().isEmpty()) | if (getTargetLocationString().isEmpty()) | ||||
getTargetLocationValue() = getDefaultBuildsRootFolder() + "AndroidStudio"; | getTargetLocationValue() = getDefaultBuildsRootFolder() + "AndroidStudio"; | ||||
if (getGradleVersionString().isEmpty()) | |||||
getGradleVersionValue() = "2.10"; | |||||
if (getGradleWrapperVersionString().isEmpty()) | |||||
getGradleWrapperVersionValue() = "0.6.0-beta5"; | |||||
if (getGradleToolchainString().isEmpty()) | |||||
getGradleToolchainValue() = "clang"; | |||||
if (getGradleToolchainVersionString().isEmpty()) | |||||
getGradleToolchainVersionValue() = getGradleToolchainValue() == "clang" ? "3.6" : "4.9"; | |||||
if (getBuildToolsVersionString().isEmpty()) | |||||
getBuildToolsVersionValue() = "23.0.1"; | |||||
} | |||||
//============================================================================== | |||||
void createToolchainExporterProperties (PropertyListBuilder& props) override | |||||
{ | |||||
props.add (new TextPropertyComponent (getGradleVersionValue(), "gradle version", 32, false), | |||||
"The version of gradle that Android Studio should use to build this app"); | |||||
props.add (new TextPropertyComponent (getGradleWrapperVersionValue(), "gradle-experimental wrapper version", 32, false), | |||||
"The version of the gradle-experimental wrapper that Android Studio should use to build this app"); | |||||
static const char* toolchains[] = { "clang", "gcc", nullptr }; | |||||
props.add (new ChoicePropertyComponent (getGradleToolchainValue(), "NDK Toolchain", StringArray (toolchains), Array<var> (toolchains)), | |||||
"The toolchain that gradle should invoke for NDK compilation (variable model.android.ndk.tooclhain in app/build.gradle)"); | |||||
props.add (new TextPropertyComponent (getGradleToolchainVersionValue(), "NDK Toolchain version", 32, false), | |||||
"The version number of the toolchainthat gradle should invoke for NDK compilation (variable model.android.ndk.tooclhainVersion in app/build.gradle)"); | |||||
} | |||||
void createLibraryModuleExporterProperties (PropertyListBuilder&) override | |||||
{ | |||||
// gradle cannot do native library modules as far as we know... | |||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
@@ -69,33 +129,13 @@ public: | |||||
return androidStudioExecutable.startAsProcess ("\"" + targetFolder.getFullPathName() + "\""); | return androidStudioExecutable.startAsProcess ("\"" + targetFolder.getFullPathName() + "\""); | ||||
} | } | ||||
void createExporterProperties (PropertyListBuilder& props) override | |||||
{ | |||||
AndroidProjectExporterBase::createExporterProperties (props); | |||||
props.add (new TextPropertyComponent (getNDKPlatformVersionValue(), "NDK Platform Version", 32, false), | |||||
"The value to use for android$user.ndk.platformVersion in Gradle"); | |||||
props.add (new TextPropertyComponent (getBuildToolsVersionValue(), "Build Tools Version", 32, false), | |||||
"The version of build tools use for build tools in Gradle"); | |||||
} | |||||
Value getNDKPlatformVersionValue() { return getSetting (Ids::androidNdkPlatformVersion); } | Value getNDKPlatformVersionValue() { return getSetting (Ids::androidNdkPlatformVersion); } | ||||
String getNDKPlatformVersionString() const { return settings [Ids::androidNdkPlatformVersion]; } | String getNDKPlatformVersionString() const { return settings [Ids::androidNdkPlatformVersion]; } | ||||
Value getBuildToolsVersionValue() { return getSetting (Ids::buildToolsVersion); } | Value getBuildToolsVersionValue() { return getSetting (Ids::buildToolsVersion); } | ||||
String getBuildToolsVersionString() const { return settings [Ids::buildToolsVersion]; } | String getBuildToolsVersionString() const { return settings [Ids::buildToolsVersion]; } | ||||
void removeOldFiles (const File& targetFolder) const | |||||
{ | |||||
targetFolder.getChildFile ("app/src").deleteRecursively(); | |||||
targetFolder.getChildFile ("app/build").deleteRecursively(); | |||||
targetFolder.getChildFile ("app/build.gradle").deleteFile(); | |||||
targetFolder.getChildFile ("gradle").deleteRecursively(); | |||||
targetFolder.getChildFile ("local.properties").deleteFile(); | |||||
targetFolder.getChildFile ("settings.gradle").deleteFile(); | |||||
} | |||||
//========================================================================== | |||||
void create (const OwnedArray<LibraryModule>& modules) const override | void create (const OwnedArray<LibraryModule>& modules) const override | ||||
{ | { | ||||
const File targetFolder (getTargetFolder()); | const File targetFolder (getTargetFolder()); | ||||
@@ -110,18 +150,36 @@ public: | |||||
copyActivityJavaFiles (modules, javaTarget, package); | copyActivityJavaFiles (modules, javaTarget, package); | ||||
} | } | ||||
writeSettingsDotGradle (targetFolder); | |||||
writeLocalDotProperties (targetFolder); | |||||
writeBuildDotGradleRoot (targetFolder); | |||||
writeBuildDotGradleApp (targetFolder); | |||||
writeGradleWrapperProperties (targetFolder); | |||||
writeAndroidManifest (targetFolder); | |||||
writeStringsXML (targetFolder); | |||||
writeAppIcons (targetFolder); | |||||
writeFile (targetFolder, "settings.gradle", getSettingsGradleFileContent()); | |||||
writeFile (targetFolder, "build.gradle", getProjectBuildGradleFileContent()); | |||||
writeFile (targetFolder, "app/build.gradle", getAppBuildGradleFileContent()); | |||||
writeFile (targetFolder, "local.properties", getLocalPropertiesFileContent()); | |||||
writeFile (targetFolder, "gradle/wrapper/gradle-wrapper.properties", getGradleWrapperPropertiesFileContent()); | |||||
createSourceSymlinks (targetFolder); | |||||
writeAndroidManifest (targetFolder); | |||||
writeStringsXML (targetFolder); | |||||
writeAppIcons (targetFolder); | |||||
createSourceSymlinks (targetFolder); | |||||
} | |||||
void removeOldFiles (const File& targetFolder) const | |||||
{ | |||||
targetFolder.getChildFile ("app/src").deleteRecursively(); | |||||
targetFolder.getChildFile ("app/build").deleteRecursively(); | |||||
targetFolder.getChildFile ("app/build.gradle").deleteFile(); | |||||
targetFolder.getChildFile ("gradle").deleteRecursively(); | |||||
targetFolder.getChildFile ("local.properties").deleteFile(); | |||||
targetFolder.getChildFile ("settings.gradle").deleteFile(); | |||||
} | |||||
void writeFile (const File& gradleProjectFolder, const String& filePath, const String& fileContent) const | |||||
{ | |||||
MemoryOutputStream outStream; | |||||
outStream << fileContent; | |||||
overwriteFileIfDifferentOrThrow (gradleProjectFolder.getChildFile (filePath), outStream); | |||||
} | } | ||||
//========================================================================== | |||||
static File findAndroidStudioExecutable() | static File findAndroidStudioExecutable() | ||||
{ | { | ||||
#if JUCE_WINDOWS | #if JUCE_WINDOWS | ||||
@@ -190,12 +248,12 @@ protected: | |||||
} | } | ||||
private: | private: | ||||
static void createSymboicLinkAndCreateParentFolders (const File& originalFile, const File& linkFile) | |||||
static void createSymlinkAndParentFolders (const File& originalFile, const File& linkFile) | |||||
{ | { | ||||
{ | { | ||||
const File linkFileParentDirectory (linkFile.getParentDirectory()); | const File linkFileParentDirectory (linkFile.getParentDirectory()); | ||||
// this will recursively creative the parent directories for the file | |||||
// this will recursively creative the parent directories for the file. | |||||
// without this, the symlink would fail because it doesn't automatically create | // without this, the symlink would fail because it doesn't automatically create | ||||
// the folders if they don't exist | // the folders if they don't exist | ||||
if (! linkFileParentDirectory.createDirectory()) | if (! linkFileParentDirectory.createDirectory()) | ||||
@@ -228,7 +286,7 @@ private: | |||||
const File originalFile (projectItem.getFile()); | const File originalFile (projectItem.getFile()); | ||||
const File targetFile (targetFolder.getChildFile (originalFile.getFileName())); | const File targetFile (targetFolder.getChildFile (originalFile.getFileName())); | ||||
createSymboicLinkAndCreateParentFolders (originalFile, targetFile); | |||||
createSymlinkAndParentFolders (originalFile, targetFile); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -259,15 +317,6 @@ private: | |||||
writeIcons (folder.getChildFile ("app/src/main/res/")); | writeIcons (folder.getChildFile ("app/src/main/res/")); | ||||
} | } | ||||
void writeSettingsDotGradle (const File& folder) const | |||||
{ | |||||
MemoryOutputStream memoryOutputStream; | |||||
memoryOutputStream << "include ':app'"; | |||||
overwriteFileIfDifferentOrThrow (folder.getChildFile ("settings.gradle"), memoryOutputStream); | |||||
} | |||||
static String sanitisePath (String path) | static String sanitisePath (String path) | ||||
{ | { | ||||
return expandHomeFolderToken (path).replace ("\\", "\\\\"); | return expandHomeFolderToken (path).replace ("\\", "\\\\"); | ||||
@@ -281,327 +330,449 @@ private: | |||||
.replace ("~", homeFolder); | .replace ("~", homeFolder); | ||||
} | } | ||||
void writeLocalDotProperties (const File& folder) const | |||||
//========================================================================== | |||||
struct GradleElement | |||||
{ | { | ||||
MemoryOutputStream memoryOutputStream; | |||||
virtual ~GradleElement() {} | |||||
String toString() const { return toStringIndented (0); } | |||||
virtual String toStringIndented (int indentLevel) const = 0; | |||||
memoryOutputStream << "ndk.dir=" << sanitisePath (getNDKPathString()) << newLine | |||||
<< "sdk.dir=" << sanitisePath (getSDKPathString()); | |||||
static String indent (int indentLevel) { return String::repeatedString (" ", indentLevel); } | |||||
}; | |||||
overwriteFileIfDifferentOrThrow (folder.getChildFile ("local.properties"), memoryOutputStream); | |||||
} | |||||
//========================================================================== | |||||
struct GradleStatement : public GradleElement | |||||
{ | |||||
GradleStatement (const String& s) : statement (s) {} | |||||
String toStringIndented (int indentLevel) const override { return indent (indentLevel) + statement; } | |||||
String statement; | |||||
}; | |||||
//========================================================================== | |||||
struct GradleCppFlag : public GradleStatement | |||||
{ | |||||
GradleCppFlag (const String& flag) | |||||
: GradleStatement ("cppFlags.add(" + flag.quoted() + ")") {} | |||||
}; | |||||
void writeGradleWrapperProperties (const File& folder) const | |||||
struct GradlePreprocessorDefine : public GradleStatement | |||||
{ | { | ||||
MemoryOutputStream memoryOutputStream; | |||||
GradlePreprocessorDefine (const String& define, const String& value) | |||||
: GradleStatement ("cppFlags.add(\"-D" + define + "=" + value + "\")") {} | |||||
}; | |||||
memoryOutputStream << "distributionUrl=https\\://services.gradle.org/distributions/gradle-2.10-all.zip"; | |||||
struct GradleHeaderIncludePath : public GradleStatement | |||||
{ | |||||
GradleHeaderIncludePath (const String& path) | |||||
: GradleStatement ("cppFlags.add(\"-I" + sanitisePath (path) + "\".toString())") {} | |||||
}; | |||||
overwriteFileIfDifferentOrThrow (folder.getChildFile ("gradle/wrapper/gradle-wrapper.properties"), memoryOutputStream); | |||||
} | |||||
struct GradleLibrarySearchPath : public GradleStatement | |||||
{ | |||||
GradleLibrarySearchPath (const String& path) | |||||
: GradleStatement ("cppFlags.add(\"-L" + sanitisePath (path) + "\".toString())") {} | |||||
}; | |||||
void writeBuildDotGradleRoot (const File& folder) const | |||||
struct GradleLinkerFlag : public GradleStatement | |||||
{ | { | ||||
MemoryOutputStream memoryOutputStream; | |||||
GradleLinkerFlag (const String& flag) | |||||
: GradleStatement ("ldFlags.add(" + flag.quoted() + "\")") {} | |||||
}; | |||||
const String indent = getIndentationString(); | |||||
struct GradleLinkLibrary : public GradleStatement | |||||
{ | |||||
GradleLinkLibrary (const String& lib) | |||||
: GradleStatement ("ldLibs.add(" + lib.quoted() + ")") {} | |||||
}; | |||||
// this is needed to make sure the correct version of the gradle build tools is available. | |||||
// needs to be kept up to date! | |||||
memoryOutputStream << "buildscript {" << newLine | |||||
<< indent << "repositories {" << newLine | |||||
<< indent << indent << "jcenter()" << newLine | |||||
<< indent << "}" << newLine | |||||
<< indent << "dependencies {" << newLine | |||||
<< indent << indent << "classpath 'com.android.tools.build:gradle-experimental:0.6.0-beta5'" << newLine | |||||
<< indent << "}" << newLine | |||||
<< "}" << newLine | |||||
<< newLine | |||||
<< "allprojects {" << newLine | |||||
<< indent << "repositories {" << newLine | |||||
<< indent << indent << "jcenter()" << newLine | |||||
<< indent << "}" << newLine | |||||
<< "}"; | |||||
//========================================================================== | |||||
struct GradleValue : public GradleElement | |||||
{ | |||||
template <typename ValueType> | |||||
GradleValue (const String& k, const ValueType& v) | |||||
: key (k), value (v) {} | |||||
overwriteFileIfDifferentOrThrow (folder.getChildFile ("build.gradle"), memoryOutputStream); | |||||
} | |||||
GradleValue (const String& k, bool boolValue) | |||||
: key (k), value (boolValue ? "true" : "false") {} | |||||
void writeStringsXML (const File& folder) const | |||||
String toStringIndented (int indentLevel) const override | |||||
{ | |||||
return indent (indentLevel) + key + " = " + value; | |||||
} | |||||
protected: | |||||
String key, value; | |||||
}; | |||||
struct GradleString : public GradleValue | |||||
{ | { | ||||
XmlElement strings ("resources"); | |||||
XmlElement* resourceName = strings.createNewChildElement ("string"); | |||||
GradleString (const String& k, const String& str) | |||||
: GradleValue (k, str.quoted()) | |||||
{ | |||||
if (str.containsAnyOf ("${\"\'")) | |||||
value += ".toString()"; | |||||
} | |||||
}; | |||||
resourceName->setAttribute ("name", "app_name"); | |||||
resourceName->addTextElement (projectName); | |||||
struct GradleFilePath : public GradleValue | |||||
{ | |||||
GradleFilePath (const String& k, const String& path) | |||||
: GradleValue (k, "new File(\"" + sanitisePath (path) + "\")") {} | |||||
}; | |||||
writeXmlOrThrow (strings, folder.getChildFile ("app/src/main/res/values/string.xml"), "utf-8", 100, true); | |||||
//========================================================================== | |||||
struct GradleObject : public GradleElement | |||||
{ | |||||
GradleObject (const String& nm) : name (nm) {} | |||||
template <typename GradleType, typename... Args> | |||||
void add (Args... args) | |||||
{ | |||||
children.add (new GradleType (std::forward<Args> (args)...)); | |||||
} | |||||
void addChildObject (GradleObject* objectToAdd) noexcept | |||||
{ | |||||
children.add (objectToAdd); | |||||
} | |||||
String toStringIndented (int indentLevel) const override | |||||
{ | |||||
String result; | |||||
result << indent (indentLevel) << name << " {" << newLine; | |||||
for (const auto& child : children) | |||||
result << child->toStringIndented (indentLevel + 1) << newLine; | |||||
result << indent (indentLevel) << "}"; | |||||
if (indentLevel == 0) | |||||
result << newLine; | |||||
return result; | |||||
} | |||||
private: | |||||
String name; | |||||
OwnedArray<GradleElement> children; | |||||
}; | |||||
//========================================================================== | |||||
String getSettingsGradleFileContent() const | |||||
{ | |||||
return "include ':app'"; | |||||
} | } | ||||
void writeAndroidManifest (const File& folder) const | |||||
String getProjectBuildGradleFileContent() const | |||||
{ | { | ||||
ScopedPointer<XmlElement> manifest (createManifestXML()); | |||||
String projectBuildGradle; | |||||
projectBuildGradle << getGradleBuildScript(); | |||||
projectBuildGradle << getGradleAllProjects(); | |||||
writeXmlOrThrow (*manifest, folder.getChildFile ("app/src/main/AndroidManifest.xml"), "utf-8", 100, true); | |||||
return projectBuildGradle; | |||||
} | } | ||||
String createModelDotAndroid (const String& indent, | |||||
const String& minimumSDKVersion, | |||||
const String& buildToolsVersion, | |||||
const String& bundleIdentifier) const | |||||
//========================================================================== | |||||
String getGradleBuildScript() const | |||||
{ | { | ||||
String result; | |||||
GradleObject buildScript ("buildscript"); | |||||
result << "android {" << newLine | |||||
<< indent << "compileSdkVersion = " << minimumSDKVersion << newLine | |||||
<< indent << "buildToolsVersion = \"" << buildToolsVersion << "\"" << newLine | |||||
<< indent << "defaultConfig.with {" << newLine | |||||
<< indent << indent << "applicationId = \"" << bundleIdentifier.toLowerCase() << "\"" << newLine | |||||
<< indent << indent << "minSdkVersion.apiLevel = " << minimumSDKVersion << newLine | |||||
<< indent << indent << "targetSdkVersion.apiLevel = " << minimumSDKVersion << newLine | |||||
<< indent << "}" << newLine | |||||
<< "}" << newLine; | |||||
buildScript.addChildObject (getGradleRepositories()); | |||||
buildScript.addChildObject (getGradleDependencies()); | |||||
return result; | |||||
return buildScript.toString(); | |||||
} | } | ||||
String createModelDotAndroidSources (const String& indent) const | |||||
GradleObject* getGradleRepositories() const | |||||
{ | { | ||||
String result; | |||||
auto repositories = new GradleObject ("repositories"); | |||||
repositories->add<GradleStatement> ("jcenter()"); | |||||
return repositories; | |||||
} | |||||
result << "android.sources {" << newLine | |||||
<< indent << "main {" << newLine | |||||
<< indent << indent << "jni {" << newLine | |||||
<< indent << indent << indent << "source {" << newLine | |||||
<< indent << indent << indent << indent << "exclude \"**/JuceModules/\"" << newLine | |||||
<< indent << indent << indent << "}" << newLine | |||||
<< indent << indent << "}" << newLine | |||||
<< indent << "}" << newLine | |||||
<< "}" << newLine; | |||||
GradleObject* getGradleDependencies() const | |||||
{ | |||||
auto dependencies = new GradleObject ("dependencies"); | |||||
return result; | |||||
dependencies->add<GradleStatement> ("classpath 'com.android.tools.build:gradle-experimental:" | |||||
+ getGradleWrapperVersionString() + "'"); | |||||
return dependencies; | |||||
} | } | ||||
struct ShouldBeAddedToProjectPredicate | |||||
String getGradleAllProjects() const | |||||
{ | { | ||||
bool operator() (const Project::Item& projectItem) const { return projectItem.shouldBeAddedToTargetProject(); } | |||||
}; | |||||
GradleObject allProjects ("allprojects"); | |||||
allProjects.addChildObject (getGradleRepositories()); | |||||
return allProjects.toString(); | |||||
} | |||||
StringArray getCPPFlags() const | |||||
//========================================================================== | |||||
String getAppBuildGradleFileContent() const | |||||
{ | { | ||||
StringArray result; | |||||
String appBuildGradle; | |||||
result.add ("\"-fsigned-char\""); | |||||
result.add ("\"-fexceptions\""); | |||||
result.add ("\"-frtti\""); | |||||
appBuildGradle << "apply plugin: 'com.android.model.application'" << newLine; | |||||
appBuildGradle << getAndroidModel(); | |||||
appBuildGradle << getAppDependencies(); | |||||
if (isCPP11Enabled()) | |||||
result.add ("\"-std=c++11\""); | |||||
return appBuildGradle; | |||||
} | |||||
StringArray extraFlags (StringArray::fromTokens (getExtraCompilerFlagsString(), " ", "")); | |||||
String getAndroidModel() const | |||||
{ | |||||
GradleObject model ("model"); | |||||
for (int i = 0; i < extraFlags.size(); ++i) | |||||
result.add (String ("\"") + extraFlags[i] + "\""); | |||||
model.addChildObject (getAndroidObject()); | |||||
model.addChildObject (getAndroidNdkSettings()); | |||||
model.addChildObject (getAndroidSources()); | |||||
model.addChildObject (getAndroidBuildConfigs()); | |||||
model.addChildObject (getAndroidSigningConfigs()); | |||||
model.addChildObject (getAndroidProductFlavours()); | |||||
// include paths | |||||
return model.toString(); | |||||
} | |||||
result.add ("\"-I${project.rootDir}/app\".toString()"); | |||||
result.add ("\"-I${ext.juceRootDir}\".toString()"); | |||||
result.add ("\"-I${ext.juceModuleDir}\".toString()"); | |||||
String getAppDependencies() const | |||||
{ | |||||
GradleObject dependencies ("dependencies"); | |||||
dependencies.add<GradleStatement> ("compile \"com.android.support:support-v4:+\""); | |||||
return dependencies.toString(); | |||||
} | |||||
{ | |||||
Array<RelativePath> cppFiles; | |||||
const Array<Project::Item>& groups = getAllGroups(); | |||||
//========================================================================== | |||||
GradleObject* getAndroidObject() const | |||||
{ | |||||
auto android = new GradleObject ("android"); | |||||
for (int i = 0; i < groups.size(); ++i) | |||||
findAllProjectItemsWithPredicate (groups.getReference (i), cppFiles, ShouldBeAddedToProjectPredicate()); | |||||
android->add<GradleValue> ("compileSdkVersion", getMinimumSDKVersionString().getIntValue()); | |||||
android->add<GradleString> ("buildToolsVersion", getBuildToolsVersionString()); | |||||
android->addChildObject (getAndroidDefaultConfig()); | |||||
for (int i = 0; i < cppFiles.size(); ++i) | |||||
{ | |||||
const RelativePath absoluteSourceFile (cppFiles.getReference (i).rebased (getTargetFolder(), | |||||
project.getProjectFolder(), | |||||
RelativePath::projectFolder)); | |||||
return android; | |||||
} | |||||
const String absoluteIncludeFolder (sanitisePath (project.getProjectFolder().getFullPathName() + "/" | |||||
+ absoluteSourceFile.toUnixStyle().upToLastOccurrenceOf ("/", false, false))); | |||||
GradleObject* getAndroidDefaultConfig() const | |||||
{ | |||||
const String bundleIdentifier = project.getBundleIdentifier().toString().toLowerCase(); | |||||
const int minSdkVersion = getMinimumSDKVersionString().getIntValue(); | |||||
result.addIfNotAlreadyThere ("\"-I" + absoluteIncludeFolder + "\".toString()"); | |||||
} | |||||
} | |||||
auto defaultConfig = new GradleObject ("defaultConfig.with"); | |||||
defaultConfig->add<GradleString> ("applicationId", bundleIdentifier); | |||||
defaultConfig->add<GradleValue> ("minSdkVersion.apiLevel", minSdkVersion); | |||||
defaultConfig->add<GradleValue> ("targetSdkVersion.apiLevel", minSdkVersion); | |||||
return result; | |||||
return defaultConfig; | |||||
} | } | ||||
StringArray getLDLibs() const | |||||
GradleObject* getAndroidNdkSettings() const | |||||
{ | { | ||||
StringArray result; | |||||
const String toolchain = getGradleToolchainString(); | |||||
const String toolchainVersion = getGradleToolchainVersionString(); | |||||
const bool isClang = (toolchain == "clang"); | |||||
result.add ("android"); | |||||
result.add ("EGL"); | |||||
result.add ("GLESv2"); | |||||
result.add ("log"); | |||||
auto ndkSettings = new GradleObject ("android.ndk"); | |||||
result.addArray (StringArray::fromTokens(getExternalLibrariesString(), ";", "")); | |||||
ndkSettings->add<GradleString> ("moduleName", "juce_jni"); | |||||
ndkSettings->add<GradleString> ("toolchain", toolchain); | |||||
ndkSettings->add<GradleValue> ("toolchainVersion", toolchainVersion); | |||||
ndkSettings->add<GradleString> ("stl", isClang ? "c++_static" : "gnustl_static"); | |||||
ndkSettings->addChildObject (getNdkJuceExtraProperties()); | |||||
return result; | |||||
addAllNdkCompilerSettings (ndkSettings); | |||||
return ndkSettings; | |||||
} | } | ||||
String createModelDotAndroidNDK (const String& indent) const | |||||
GradleObject* getNdkJuceExtraProperties() const | |||||
{ | { | ||||
String result; | |||||
const String platformVersion (getNDKPlatformVersionString()); | |||||
result << "android.ndk {" << newLine | |||||
<< indent << "moduleName = \"juce_jni\"" << newLine | |||||
<< indent << "stl = \"c++_static\"" << newLine | |||||
<< indent << "toolchain = \"clang\"" << newLine | |||||
<< indent << "toolchainVersion = 3.6" << newLine; | |||||
auto ext = new GradleObject ("ext"); | |||||
ext->add<GradleString> ("juceRootDir", "${project.rootDir}/../../../../"); | |||||
ext->add<GradleString> ("juceModuleDir", "${juceRootDir}/modules"); | |||||
return ext; | |||||
} | |||||
if (platformVersion.isNotEmpty()) | |||||
result << indent << "platformVersion = " << getNDKPlatformVersionString() << newLine; | |||||
void addAllNdkCompilerSettings (GradleObject* ndk) const | |||||
{ | |||||
addNdkCppFlags (ndk); | |||||
addNdkPreprocessorDefines (ndk); | |||||
addNdkHeaderIncludePaths (ndk); | |||||
addNdkLinkerFlags (ndk); | |||||
addNdkLibraries (ndk); | |||||
} | |||||
result << indent << "ext {" << newLine | |||||
<< indent << indent << "juceRootDir = \"" << "${project.rootDir}/../../../../" << "\".toString()" << newLine | |||||
<< indent << indent << "juceModuleDir = \"" << "${juceRootDir}/modules" << "\".toString()" << newLine | |||||
<< indent << "}" << newLine; | |||||
void addNdkCppFlags (GradleObject* ndk) const | |||||
{ | |||||
StringArray cppFlags { "-fsigned-char", "-fexceptions", "-frtti", "-std=c++11" }; | |||||
// CPP flags | |||||
cppFlags.mergeArray (StringArray::fromTokens (getExtraCompilerFlagsString(), " ", "")); | |||||
{ | |||||
StringArray cppFlags (getCPPFlags()); | |||||
for (int i = 0; i < cppFlags.size(); ++i) | |||||
ndk->add<GradleCppFlag> (cppFlags[i]); | |||||
} | |||||
for (int i = 0; i < cppFlags.size(); ++i) | |||||
result << indent << "cppFlags.add(" << cppFlags[i] << ")" << newLine; | |||||
} | |||||
void addNdkPreprocessorDefines (GradleObject* ndk) const | |||||
{ | |||||
const auto& defines = getAllPreprocessorDefs(); | |||||
// libraries | |||||
for (int i = 0; i < defines.size(); ++i) | |||||
ndk->add<GradlePreprocessorDefine> ( defines.getAllKeys()[i], defines.getAllValues()[i]); | |||||
} | |||||
{ | |||||
StringArray libraries (getLDLibs()); | |||||
void addNdkHeaderIncludePaths (GradleObject* ndk) const | |||||
{ | |||||
StringArray includePaths { "${project.rootDir}/app", "${ext.juceRootDir}", "${ext.juceModuleDir}" }; | |||||
result << indent << "ldLibs.addAll("; | |||||
auto cppFiles = getAllIncludedCppFiles(); | |||||
for (int i = 0; i < libraries.size(); ++i) | |||||
{ | |||||
result << "\"" << libraries[i] << "\""; | |||||
for (const auto& cppFile : cppFiles) | |||||
includePaths.addIfNotAlreadyThere (getIncludePathForFile (cppFile)); | |||||
if (i + 1 != libraries.size()) | |||||
result << ", "; | |||||
} | |||||
for (const auto& path : includePaths) | |||||
ndk->add<GradleHeaderIncludePath> (path); | |||||
} | |||||
result << ")" << newLine; | |||||
} | |||||
Array<RelativePath> getAllIncludedCppFiles() const | |||||
{ | |||||
Array<RelativePath> cppFiles; | |||||
const auto& groups = getAllGroups(); | |||||
result << "}" << newLine; | |||||
for (int i = 0; i < groups.size(); ++i) | |||||
findAllProjectItemsWithPredicate (groups.getReference (i), cppFiles, ShouldBeAddedToProjectPredicate()); | |||||
return result; | |||||
return cppFiles; | |||||
} | } | ||||
String getModelDotAndroidDotBuildTypesFlags (const String& indent, const ConstConfigIterator& config) const | |||||
String getIncludePathForFile (const RelativePath& file) const | |||||
{ | { | ||||
const String configName (config->getName()); | |||||
return sanitisePath (project.getProjectFolder().getFullPathName() + "/" | |||||
+ file.rebased (getTargetFolder(), | |||||
project.getProjectFolder(), | |||||
RelativePath::projectFolder) | |||||
.toUnixStyle() | |||||
.upToLastOccurrenceOf ("/", false, false)); | |||||
} | |||||
// there appears to be an issue with build types that have a name other than | |||||
// "debug" or "release". Apparently this is hard coded in Android Studio ... | |||||
void addNdkLinkerFlags (GradleObject* ndk) const | |||||
{ | |||||
const auto linkerFlags = StringArray::fromTokens (getExtraLinkerFlagsString(), " ", ""); | |||||
if (configName != "Debug" && configName != "Release") | |||||
throw SaveError ("Build configurations other than Debug and Release are not yet support for Android Studio"); | |||||
for (const auto& flag : linkerFlags) | |||||
ndk->add<GradleLinkerFlag> (flag); | |||||
StringArray rootFlags; // model.android.buildTypes.debug/release { ... } | |||||
StringArray ndkFlags; // model.android.buildTypes.debug/release.ndk.with { ... } | |||||
} | |||||
void addNdkLibraries (GradleObject* ndk) const | |||||
{ | |||||
StringArray libs {"android", "EGL", "GLESv2", "log"}; | |||||
libs.addArray (StringArray::fromTokens(getExternalLibrariesString(), ";", "")); | |||||
if (config->isDebug()) | |||||
{ | |||||
ndkFlags.add ("debuggable = true"); | |||||
ndkFlags.add ("cppFlags.add(\"-g\")"); | |||||
ndkFlags.add ("cppFlags.add(\"-DDEBUG=1\")"); | |||||
ndkFlags.add ("cppFlags.add(\"-D_DEBUG=1\")"); | |||||
} | |||||
else | |||||
{ | |||||
rootFlags.add ("signingConfig = $(\"android.signingConfigs.releaseConfig\")"); | |||||
ndkFlags.add ("cppFlags.add(\"-DNDEBUG=1\")"); | |||||
} | |||||
for (const auto& lib : libs) | |||||
ndk->add<GradleLinkLibrary> (lib); | |||||
} | |||||
const StringArray& headerSearchPaths = config->getHeaderSearchPaths(); | |||||
for (int i = 0; i < headerSearchPaths.size(); ++i) | |||||
ndkFlags.add ("cppFlags.add(\"-I" + sanitisePath (headerSearchPaths[i]) + "\".toString())"); | |||||
GradleObject* getAndroidSources() const | |||||
{ | |||||
auto source = new GradleObject ("source"); // app source folder | |||||
source->add<GradleStatement> ("exclude \"**/JuceModules/\""); | |||||
const StringArray& librarySearchPaths = config->getLibrarySearchPaths(); | |||||
for (int i = 0; i < librarySearchPaths.size(); ++i) | |||||
ndkFlags.add ("cppFlags.add(\"-L" + sanitisePath (librarySearchPaths[i]) + "\".toString())"); | |||||
auto jni = new GradleObject ("jni"); // all C++ sources for app | |||||
jni->addChildObject (source); | |||||
{ | |||||
StringPairArray preprocessorDefinitions = config->getAllPreprocessorDefs(); | |||||
preprocessorDefinitions.set ("JUCE_ANDROID", "1"); | |||||
preprocessorDefinitions.set ("JUCE_ANDROID_API_VERSION", getMinimumSDKVersionString()); | |||||
preprocessorDefinitions.set ("JUCE_ANDROID_ACTIVITY_CLASSNAME", getJNIActivityClassName().replaceCharacter ('/', '_')); | |||||
preprocessorDefinitions.set ("JUCE_ANDROID_ACTIVITY_CLASSPATH", "\\\"" + getActivityClassPath().replaceCharacter('.', '/') + "\\\""); | |||||
auto main = new GradleObject ("main"); // all sources for app | |||||
main->addChildObject (jni); | |||||
const StringArray& keys = preprocessorDefinitions.getAllKeys(); | |||||
auto sources = new GradleObject ("android.sources"); // all sources | |||||
sources->addChildObject (main); | |||||
return sources; | |||||
} | |||||
for (int i = 0; i < keys.size(); ++i) | |||||
ndkFlags.add (String ("cppFlags.add(\"-D") + keys[i] + String ("=") + preprocessorDefinitions[keys[i]] + "\")"); | |||||
} | |||||
GradleObject* getAndroidBuildConfigs() const | |||||
{ | |||||
auto buildConfigs = new GradleObject ("android.buildTypes"); | |||||
ndkFlags.add ("cppFlags.add(\"-O" + config->getGCCOptimisationFlag() + "\")"); | |||||
for (ConstConfigIterator config (*this); config.next();) | |||||
buildConfigs->addChildObject (getBuildConfig (*config)); | |||||
String result; | |||||
return buildConfigs; | |||||
} | |||||
result << configName.toLowerCase() << " {" << newLine; | |||||
GradleObject* getBuildConfig (const BuildConfiguration& config) const | |||||
{ | |||||
const String configName (config.getName()); | |||||
for (int i = 0; i < rootFlags.size(); ++i) | |||||
result << indent << rootFlags[i] << newLine; | |||||
// Note: at the moment, Android Studio only supports a "debug" and a "release" | |||||
// build config, but no custom build configs like Introjucer's other exporters do. | |||||
if (configName != "Debug" && configName != "Release") | |||||
throw SaveError ("Build configurations other than Debug and Release are not yet support for Android Studio"); | |||||
result << indent << "ndk.with {" << newLine; | |||||
auto gradleConfig = new GradleObject (configName.toLowerCase()); | |||||
for (int i = 0; i < ndkFlags.size(); ++i) | |||||
result << indent << indent << ndkFlags[i] << newLine; | |||||
if (! config.isDebug()) | |||||
gradleConfig->add<GradleValue> ("signingConfig", "$(\"android.signingConfigs.releaseConfig\")"); | |||||
result << indent << "}" << newLine | |||||
<< "}" << newLine; | |||||
addConfigNdkSettings (gradleConfig, config); | |||||
return result; | |||||
return gradleConfig; | |||||
} | } | ||||
String createModelDotAndroidDotBuildTypes (const String& indent) const | |||||
void addConfigNdkSettings (GradleObject* buildConfig, const BuildConfiguration& config) const | |||||
{ | { | ||||
String result; | |||||
auto ndkSettings = new GradleObject ("ndk.with"); | |||||
result << "android.buildTypes {" << newLine; | |||||
if (config.isDebug()) | |||||
{ | |||||
ndkSettings->add<GradleValue> ("debuggable", true); | |||||
ndkSettings->add<GradleCppFlag> ("-g"); | |||||
ndkSettings->add<GradlePreprocessorDefine> ("DEBUG", "1"); | |||||
ndkSettings->add<GradlePreprocessorDefine> ("_DEBUG", "1"); | |||||
} | |||||
else | |||||
{ | |||||
ndkSettings->add<GradlePreprocessorDefine> ("NDEBUG", "1"); | |||||
} | |||||
for (ConstConfigIterator config (*this); config.next();) | |||||
result << CodeHelpers::indent (getModelDotAndroidDotBuildTypesFlags (indent, config), indent.length(), true); | |||||
ndkSettings->add<GradleCppFlag> ("-O" + config.getGCCOptimisationFlag()); | |||||
result << "}" << newLine; | |||||
for (const auto& path : config.getHeaderSearchPaths()) | |||||
ndkSettings->add<GradleHeaderIncludePath> (path); | |||||
return result; | |||||
} | |||||
for (const auto& path : config.getLibrarySearchPaths()) | |||||
ndkSettings->add<GradleLibrarySearchPath> (path); | |||||
String createModelDotAndroidDotSigningConfigs (const String& indent) const | |||||
{ | |||||
String result; | |||||
ndkSettings->add<GradlePreprocessorDefine> ("JUCE_ANDROID", "1"); | |||||
ndkSettings->add<GradlePreprocessorDefine> ("JUCE_ANDROID_API_VERSION", getMinimumSDKVersionString()); | |||||
ndkSettings->add<GradlePreprocessorDefine> ("JUCE_ANDROID_ACTIVITY_CLASSNAME", getJNIActivityClassName().replaceCharacter ('/', '_')); | |||||
ndkSettings->add<GradlePreprocessorDefine> ("JUCE_ANDROID_ACTIVITY_CLASSPATH","\\\"" + getActivityClassPath().replaceCharacter('.', '/') + "\\\""); | |||||
result << "android.signingConfigs {" << newLine | |||||
<< indent << "create(\"releaseConfig\") {" << newLine | |||||
<< indent << indent << "storeFile = new File(\"" << sanitisePath (getKeyStoreString()) << "\")" << newLine | |||||
<< indent << indent << "storePassword = \"" << getKeyStorePassString() << "\"" << newLine | |||||
<< indent << indent << "keyAlias = \"" << getKeyAliasString() << "\"" << newLine | |||||
<< indent << indent << "keyPassword = \"" << getKeyAliasPassString() << "\"" << newLine | |||||
<< indent << indent << "storeType = \"jks\"" << newLine | |||||
<< indent << "}" << newLine | |||||
<< "}" << newLine; | |||||
const auto defines = config.getAllPreprocessorDefs(); | |||||
for (int i = 0; i < defines.size(); ++i) | |||||
ndkSettings->add<GradlePreprocessorDefine> (defines.getAllKeys()[i], defines.getAllValues()[i]); | |||||
return result; | |||||
buildConfig->addChildObject (ndkSettings); | |||||
} | } | ||||
String createModelDotAndroidDotProductFlavors (const String& indent) const | |||||
GradleObject* getAndroidSigningConfigs() const | |||||
{ | { | ||||
String result; | |||||
auto releaseConfig = new GradleObject ("create(\"releaseConfig\")"); | |||||
result << "android.productFlavors {" << newLine; | |||||
releaseConfig->add<GradleFilePath> ("storeFile", getKeyStoreString()); | |||||
releaseConfig->add<GradleString> ("storePassword", getKeyStorePassString()); | |||||
releaseConfig->add<GradleString> ("keyAlias", getKeyAliasString()); | |||||
releaseConfig->add<GradleString> ("keyPassword", getKeyAliasPassString()); | |||||
releaseConfig->add<GradleString> ("storeType", "jks"); | |||||
// TODO! - this needs to be changed so that it generates seperate flags for debug and release ... | |||||
// at present, it just generates all ABIs for all build types | |||||
auto signingConfigs = new GradleObject ("android.signingConfigs"); | |||||
signingConfigs->addChildObject (releaseConfig); | |||||
// Note: no need to add a debugConfig, Android Studio will use debug.keystore by default | |||||
return signingConfigs; | |||||
} | |||||
GradleObject* getAndroidProductFlavours() const | |||||
{ | |||||
auto flavours = new GradleObject ("android.productFlavors"); | |||||
StringArray architectures (StringArray::fromTokens (getABIs<AndroidStudioBuildConfiguration> (true), " ", "")); | StringArray architectures (StringArray::fromTokens (getABIs<AndroidStudioBuildConfiguration> (true), " ", "")); | ||||
architectures.mergeArray (StringArray::fromTokens (getABIs<AndroidStudioBuildConfiguration> (false), " ", "")); | architectures.mergeArray (StringArray::fromTokens (getABIs<AndroidStudioBuildConfiguration> (false), " ", "")); | ||||
@@ -611,73 +782,71 @@ private: | |||||
for (int i = 0; i < architectures.size(); ++i) | for (int i = 0; i < architectures.size(); ++i) | ||||
{ | { | ||||
String architecture (architectures[i].trim()); | |||||
String arch (architectures[i].trim()); | |||||
if (architecture.isEmpty()) | |||||
if ((arch).isEmpty()) | |||||
continue; | continue; | ||||
result << indent << "create(\"" << architecture << "\") {" << newLine | |||||
<< indent << indent << "ndk.abiFilters.add(\"" << architecture << "\")" << newLine | |||||
<< indent << "}" << newLine; | |||||
flavours->addChildObject (getGradleProductFlavourForArch (arch)); | |||||
} | } | ||||
result << "}" << newLine; | |||||
return result; | |||||
return flavours; | |||||
} | } | ||||
String createDependencies (const String& indent) const | |||||
GradleObject* getGradleProductFlavourForArch (const String& arch) const | |||||
{ | { | ||||
String result; | |||||
auto flavour = new GradleObject ("create(\"" + arch + "\")"); | |||||
flavour->add<GradleStatement> ("ndk.abiFilters.add(\"" + arch + "\")"); | |||||
return flavour; | |||||
} | |||||
//========================================================================== | |||||
String getLocalPropertiesFileContent() const | |||||
{ | |||||
String props; | |||||
result << "dependencies {" << newLine | |||||
<< indent << "compile \"com.android.support:support-v4:+\"" << newLine // needed for ContextCompat and ActivityCompat | |||||
<< "}" << newLine; | |||||
props << "ndk.dir=" << sanitisePath (getNDKPathString()) << newLine | |||||
<< "sdk.dir=" << sanitisePath (getSDKPathString()) << newLine; | |||||
return result; | |||||
return props; | |||||
} | } | ||||
void writeBuildDotGradleApp (const File& folder) const | |||||
String getGradleWrapperPropertiesFileContent() const | |||||
{ | { | ||||
MemoryOutputStream memoryOutputStream; | |||||
String props; | |||||
const String indent = getIndentationString(); | |||||
const String minimumSDKVersion = getMinimumSDKVersionString(); | |||||
const String bundleIdentifier = project.getBundleIdentifier().toString(); | |||||
props << "distributionUrl=https\\://services.gradle.org/distributions/gradle-" | |||||
<< getGradleVersionString() << "-all.zip"; | |||||
String buildToolsVersion = getBuildToolsVersionString(); | |||||
return props; | |||||
} | |||||
if (buildToolsVersion.isEmpty()) | |||||
buildToolsVersion = "23.0.1"; | |||||
//========================================================================== | |||||
void writeStringsXML (const File& folder) const | |||||
{ | |||||
XmlElement strings ("resources"); | |||||
XmlElement* resourceName = strings.createNewChildElement ("string"); | |||||
memoryOutputStream << "apply plugin: 'com.android.model.application'" << newLine | |||||
<< newLine | |||||
<< "model {" << newLine | |||||
<< CodeHelpers::indent (createModelDotAndroid (indent, | |||||
minimumSDKVersion, | |||||
buildToolsVersion, | |||||
bundleIdentifier), indent.length(), true) | |||||
<< newLine | |||||
<< CodeHelpers::indent (createModelDotAndroidNDK (indent), indent.length(), true) | |||||
<< newLine | |||||
<< CodeHelpers::indent (createModelDotAndroidSources (indent), indent.length(), true) | |||||
<< newLine | |||||
<< CodeHelpers::indent (createModelDotAndroidDotBuildTypes (indent), indent.length(), true) | |||||
<< newLine | |||||
<< CodeHelpers::indent (createModelDotAndroidDotSigningConfigs (indent), indent.length(), true) | |||||
<< newLine | |||||
<< CodeHelpers::indent (createModelDotAndroidDotProductFlavors (indent), indent.length(), true) | |||||
<< "}" << newLine << newLine | |||||
<< createDependencies (indent); | |||||
resourceName->setAttribute ("name", "app_name"); | |||||
resourceName->addTextElement (projectName); | |||||
overwriteFileIfDifferentOrThrow (folder.getChildFile ("app/build.gradle"), memoryOutputStream); | |||||
writeXmlOrThrow (strings, folder.getChildFile ("app/src/main/res/values/string.xml"), "utf-8", 100, true); | |||||
} | } | ||||
static const char* getIndentationString() noexcept | |||||
//========================================================================== | |||||
void writeAndroidManifest (const File& folder) const | |||||
{ | { | ||||
return " "; | |||||
ScopedPointer<XmlElement> manifest (createManifestXML()); | |||||
writeXmlOrThrow (*manifest, folder.getChildFile ("app/src/main/AndroidManifest.xml"), "utf-8", 100, true); | |||||
} | } | ||||
//========================================================================== | |||||
struct ShouldBeAddedToProjectPredicate | |||||
{ | |||||
bool operator() (const Project::Item& projectItem) const { return projectItem.shouldBeAddedToTargetProject(); } | |||||
}; | |||||
//========================================================================== | |||||
const File androidStudioExecutable; | const File androidStudioExecutable; | ||||
JUCE_DECLARE_NON_COPYABLE (AndroidStudioProjectExporter) | JUCE_DECLARE_NON_COPYABLE (AndroidStudioProjectExporter) | ||||
@@ -29,8 +29,9 @@ | |||||
#include "jucer_ProjectExport_Make.h" | #include "jucer_ProjectExport_Make.h" | ||||
#include "jucer_ProjectExport_MSVC.h" | #include "jucer_ProjectExport_MSVC.h" | ||||
#include "jucer_ProjectExport_XCode.h" | #include "jucer_ProjectExport_XCode.h" | ||||
#include "jucer_ProjectExport_Android.h" | |||||
#include "jucer_ProjectExport_AndroidBase.h" | |||||
#include "jucer_ProjectExport_AndroidStudio.h" | #include "jucer_ProjectExport_AndroidStudio.h" | ||||
#include "jucer_ProjectExport_AndroidAnt.h" | |||||
#include "jucer_ProjectExport_CodeBlocks.h" | #include "jucer_ProjectExport_CodeBlocks.h" | ||||
//============================================================================== | //============================================================================== | ||||
@@ -143,7 +143,6 @@ namespace Ids | |||||
DECLARE_ID (androidNDKPath); | DECLARE_ID (androidNDKPath); | ||||
DECLARE_ID (androidInternetNeeded); | DECLARE_ID (androidInternetNeeded); | ||||
DECLARE_ID (androidArchitectures); | DECLARE_ID (androidArchitectures); | ||||
DECLARE_ID (androidCpp11); | |||||
DECLARE_ID (androidMicNeeded); | DECLARE_ID (androidMicNeeded); | ||||
DECLARE_ID (androidBluetoothNeeded); | DECLARE_ID (androidBluetoothNeeded); | ||||
DECLARE_ID (androidMinimumSDK); | DECLARE_ID (androidMinimumSDK); | ||||
@@ -158,6 +157,10 @@ namespace Ids | |||||
DECLARE_ID (androidNdkPlatformVersion); | DECLARE_ID (androidNdkPlatformVersion); | ||||
DECLARE_ID (androidScreenOrientation); | DECLARE_ID (androidScreenOrientation); | ||||
DECLARE_ID (buildToolsVersion); | DECLARE_ID (buildToolsVersion); | ||||
DECLARE_ID (gradleVersion); | |||||
DECLARE_ID (gradleWrapperVersion); | |||||
DECLARE_ID (gradleToolchain); | |||||
DECLARE_ID (gradleToolchainVersion); | |||||
DECLARE_ID (font); | DECLARE_ID (font); | ||||
DECLARE_ID (colour); | DECLARE_ID (colour); | ||||
DECLARE_ID (userNotes); | DECLARE_ID (userNotes); | ||||