Browse Source

Atomic C# iOS
Squashed commits:
[82410ba] Updating Linux Build
[38113c5] Build updates for Windows
[9024b32] Handle iOS AtomicNET reference on binary editor
[6a2b2d6] Map to AnyCPU for iPhone libraries, which aren't executables
[1f9bc0f] ./Build_AtomicEditor.sh --with-ios --with-android working on Mac
[1807791] Running on iOS
[10a6d4f] WIP Atomic C# iOS support

Josh Engebretson 9 years ago
parent
commit
1ec8f1037e
40 changed files with 1497 additions and 373 deletions
  1. 58 0
      Build/CMake/IOS/AtomicNET.framework.plist
  2. 60 0
      Build/Scripts/Bootstrap.js
  3. 13 8
      Build/Scripts/BuildAndroid.js
  4. 44 0
      Build/Scripts/BuildAtomicNET.js
  5. 48 0
      Build/Scripts/BuildIOS.js
  6. 126 63
      Build/Scripts/BuildLinux.js
  7. 114 59
      Build/Scripts/BuildMac.js
  8. 107 58
      Build/Scripts/BuildWindows.js
  9. 2 0
      Build/Scripts/Host.js
  10. 24 0
      Build/Scripts/HostCommon.js
  11. 235 0
      Build/Scripts/Minimist.js
  12. 1 1
      Build/Scripts/Windows/CompileAndroid.bat
  13. 3 0
      Build/Scripts/Windows/CompileAtomicEditorPhase1.bat
  14. 2 0
      Build/Scripts/Windows/CompileAtomicEditorPhase2.bat
  15. 1 36
      Build_AtomicEditor.bat
  16. 3 17
      Build_AtomicEditor.sh
  17. 8 1
      Script/AtomicNET/AtomicNET/Application/Application.cs
  18. 2 7
      Script/AtomicNET/AtomicNET/Core/AtomicNET.cs
  19. 3 1
      Script/AtomicNET/AtomicNET/Core/Constants.cs
  20. 30 13
      Script/AtomicNET/AtomicNET/Core/NativeCore.cs
  21. 0 18
      Script/AtomicNET/AtomicNET/Core/NativeEvents.cs
  22. 144 0
      Script/AtomicNET/AtomicNET/Core/SDLEvents.cs
  23. 8 0
      Script/AtomicNET/AtomicNET/Graphics/Graphics.cs
  24. 26 0
      Script/AtomicNET/AtomicNETProject.json
  25. 0 7
      Script/AtomicNET/AtomicNETService/Program.cs
  26. 31 0
      Script/AtomicNET/AtomicProject.json
  27. 10 0
      Script/AtomicNET/Platform/iOS/ApiDefinition/ApiDefinition.cs
  28. 3 0
      Script/AtomicNET/Platform/iOS/AtomicNET/AtomicNETNative.framework.linkwith.cs
  29. 29 0
      Source/AtomicNET/NETNative/CMakeLists.txt
  30. 8 6
      Source/AtomicNET/NETNative/NETAtomicPlayer.cpp
  31. 53 2
      Source/AtomicNET/NETNative/NETCInterop.cpp
  32. 1 1
      Source/AtomicPlayer/Application/CMakeLists.txt
  33. 1 1
      Source/CMakeLists.txt
  34. 43 9
      Source/ToolCore/Command/NETCmd.cpp
  35. 2 2
      Source/ToolCore/Command/NETCmd.h
  36. 76 32
      Source/ToolCore/NETTools/NETBuildSystem.cpp
  37. 6 5
      Source/ToolCore/NETTools/NETBuildSystem.h
  38. 157 23
      Source/ToolCore/NETTools/NETProjectGen.cpp
  39. 13 2
      Source/ToolCore/NETTools/NETProjectGen.h
  40. 2 1
      Source/ToolCore/Project/ProjectSettings.cpp

+ 58 - 0
Build/CMake/IOS/AtomicNET.framework.plist

@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>CFBundleDevelopmentRegion</key>
+        <string>en</string>
+        <key>CFBundleIdentifier</key>
+        <string>com.atomicgameengine.atomicnetframework</string>
+        <key>CFBundleInfoDictionaryVersion</key>
+        <string>6.0</string>
+        <key>CFBundleName</key>
+        <string>Mono</string>
+        <key>CFBundlePackageType</key>
+        <string>FMWK</string>
+        <key>CFBundleShortVersionString</key>
+        <string>1.0</string>
+        <key>CFBundleSignature</key>
+        <string>????</string>
+        <key>CFBundleVersion</key>
+        <string>3.12</string>
+        <key>NSPrincipalClass</key>
+        <string></string>
+        <key>CFBundleExecutable</key>
+        <string>AtomicNETNative</string>
+
+        <key>BuildMachineOSBuild</key>
+        <string>13F34</string>
+        <key>CFBundleDevelopmentRegion</key>
+        <string>en</string>
+        <key>CFBundleSupportedPlatforms</key>
+        <array>
+                <string>iPhoneOS</string>
+        </array>
+        <key>DTCompiler</key>
+        <string>com.apple.compilers.llvm.clang.1_0</string>
+        <key>DTPlatformBuild</key>
+        <string>12D508</string>
+        <key>DTPlatformName</key>
+        <string>iphoneos</string>
+        <key>DTPlatformVersion</key>
+        <string>8.2</string>
+        <key>DTSDKBuild</key>
+        <string>12D508</string>
+        <key>DTSDKName</key>
+        <string>iphoneos8.2</string>
+        <key>DTXcode</key>
+        <string>0620</string>
+        <key>DTXcodeBuild</key>
+        <string>6C131e</string>
+        <key>MinimumOSVersion</key>
+        <string>8.0</string>
+        <key>UIDeviceFamily</key>
+        <array>
+                <integer>1</integer>
+                <integer>2</integer>
+        </array>
+</dict>
+</plist>

+ 60 - 0
Build/Scripts/Bootstrap.js

@@ -1 +1,61 @@
+
+var os = require('os');
+
+// Parse args
+var options = require('./Minimist')(process.argv.slice(2));
+var cmd = options._[0];
+
+// Load `jake` global
+require('../node_modules/jake/lib/jake');
+
+// Load jake tasks, etc
 var host = require('./Host');
+
+// Make options availabe to host
+host.options = options;
+
+function printHelp() {
+
+    console.log("\nAtomic Editor Build Script")
+    console.log("--------------------------")
+    console.log("--help         : This help text")
+    console.log("--with-android : Build with Android platform support");
+    console.log("--with-ios     : Build with iOS platform support");
+    console.log("--debug        : Build debug version of the editor and associated platform runtimes")
+    console.log("--noclean      : Do not clean before building, useful during development")
+    console.log("--nonet        : Build without AtomicNET C# scripting support")
+    console.log("--------------------------")
+
+    process.exit(0);
+}
+
+if (options["help"]) {
+    printHelp();
+}
+
+// Atomic Editor Build
+if (cmd == "buildeditor") {
+
+    console.log("\n\nBuilding Atomic Editor, this process will take a few minutes\n");
+
+    var buildTask = jake.Task['build:atomiceditor'];
+
+    if (options["with-android"]) {
+
+        if (!process.env.ANDROID_NDK) {
+            console.log("\nANDROID_NDK environment variable not set, exiting\n");
+            process.exit(1);
+        }
+    }
+
+    if (options["with-ios"]) {
+
+        if (os.platform() != "darwin") {
+            console.log("\niOS platform requires macOS, exiting\n");
+            process.exit(1);
+        }
+    }
+
+    buildTask.invoke();
+
+}

+ 13 - 8
Build/Scripts/BuildAndroid.js

@@ -2,8 +2,8 @@ var fs = require('fs-extra');
 var path = require("path");
 var host = require("./Host");
 var os = require('os');
-var atomicRoot = host.atomicRoot;
 
+var atomicRoot = host.atomicRoot;
 var buildDir = host.artifactsRoot + "Build/Android/";
 
 namespace('build', function() {
@@ -12,34 +12,39 @@ namespace('build', function() {
         async: true
     }, function() {
 
-        // Clean build
-        common.cleanCreateDir(buildDir);
+        var options = host.options;
+        var cleanBuild = options["noclean"] ? false : true;
+        var debug = options["debug"] ? true : false;
+        var config = debug ? "Debug" : "Release";
+
+
+        host.setupDirs(cleanBuild, [buildDir]);
 
         process.chdir(buildDir);
 
         var cmds = [];
 
         if (os.platform() == "win32") {
-            cmds.push(atomicRoot + "Build/Scripts/Windows/CompileAndroid.bat");
+            cmds.push(atomicRoot + "Build/Scripts/Windows/CompileAndroid.bat " + config);
         }
         else {
-            cmds.push("cmake -G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=../../../Build/CMake/Toolchains/android.toolchain.cmake -DCMAKE_BUILD_TYPE=Release ../../../");
+            cmds.push("cmake -G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=../../../Build/CMake/Toolchains/android.toolchain.cmake -DCMAKE_BUILD_TYPE="  + config + " ../../../");
             cmds.push("make -j4");
         }
 
         jake.exec(cmds, function() {
 
-            var editorAppFolder = host.artifactsRoot + (os.platform() == "win32" ? "AtomicEditor/" : "AtomicEditor/AtomicEditor.app/");
+            var editorResourceFolder = host.artifactsRoot + (os.platform() == "win32" ? "AtomicEditor/Resources/" : "AtomicEditor/AtomicEditor.app/Contents/Resources/");
 
             // Install Deployment
             fs.copySync(buildDir + "Source/AtomicPlayer/Application/libAtomicPlayer.so",
-            editorAppFolder + "Contents/Resources/ToolData/Deployment/Android/libs/armeabi-v7a/libAtomicPlayer.so");
+            editorResourceFolder + "ToolData/Deployment/Android/libs/armeabi-v7a/libAtomicPlayer.so");
 
             complete();
 
         }, {
             printStdout: true,
-            breakOnError : false
+            printStderr: true
         });
 
     });

+ 44 - 0
Build/Scripts/BuildAtomicNET.js

@@ -0,0 +1,44 @@
+var fs = require('fs-extra');
+var path = require("path");
+var host = require("./Host");
+var os = require('os');
+var atomicRoot = host.atomicRoot;
+
+namespace('build', function() {
+
+    task('atomicnet', {
+        async: true
+    }, function() {
+
+        var options = host.options;
+
+        var android = options["with-android"] ? true : false;
+        var ios = options["with-ios"] ? true : false;
+        var debug = options["debug"] ? true : false;
+
+        var cmds = [];
+
+        platforms = "-platform desktop";
+        if (android)
+            platforms += " -platform android";
+        if (ios)
+            platforms += " -platform ios";
+
+        var netCmd = host.atomicTool + " net compile " + atomicRoot + "Script/AtomicNET/AtomicNETProject.json " + platforms + " -config " + (debug ? "Debug" : "Release");
+
+        console.log(netCmd);
+        
+        cmds.push(netCmd);
+
+        jake.exec(cmds, function() {
+
+            complete();
+
+        }, {
+            printStdout: true,
+            printStderr: true
+        });
+
+    });
+
+}); // end of build namespace

+ 48 - 0
Build/Scripts/BuildIOS.js

@@ -0,0 +1,48 @@
+var fs = require('fs-extra');
+var path = require("path");
+var host = require("./Host");
+var os = require('os');
+
+var atomicRoot = host.atomicRoot;
+var buildDir = host.artifactsRoot + "Build/IOS/";
+
+namespace('build', function() {
+
+    task('ios_native', {
+        async: true
+    }, function() {
+
+        var options = host.options;
+        var cleanBuild = options["noclean"] ? false : true;
+        var debug = options["debug"] ? true : false;
+
+        var NETNativeSrcDir = buildDir + "Source/AtomicNET/NETNative/" + (debug ? "Debug" : "Release") + "-iphoneos/";
+        var NETNativeDestDir = host.artifactsRoot + "AtomicNET/" + (debug ? "Debug" : "Release") + "/Native/iOS/";
+
+        host.setupDirs(cleanBuild, [buildDir, NETNativeDestDir]);
+
+        process.chdir(buildDir);
+
+        var cmds = [];
+
+        cmds.push("cmake -DIOS=1 -DATOMIC_DEV_BUILD=0 -G Xcode ../../../");
+        cmds.push("xcodebuild -configuration " + (debug ? "Debug" : "Release") + " -parallelizeTargets -jobs 4");
+        //cmds.push("cd \"" + NETNativeSrcDir + "\" && install_name_tool -id @rpath/AtomicNETNative.framework/AtomicNETNative AtomicNETNative.framework/AtomicNETNative");
+        //cmds.push("cd \"" + NETNativeSrcDir + "\" && codesign --deep --force --verify --sign \"iPhone Developer\" ./AtomicNETNative.framework/");
+        cmds.push("cd \"" + NETNativeSrcDir + "\" && zip -r AtomicNETNative.framework.zip AtomicNETNative.framework");
+
+        jake.exec(cmds, function() {
+
+            fs.copySync(NETNativeSrcDir + "AtomicNETNative.framework", NETNativeDestDir + "AtomicNETNative.framework");
+            fs.copySync(NETNativeSrcDir + "AtomicNETNative.framework.zip", NETNativeDestDir + "AtomicNETNative.framework.zip");
+
+            complete();
+
+        }, {
+            printStdout: true,
+            printStderr: true
+        });
+
+    });
+
+}); // end of build namespace

+ 126 - 63
Build/Scripts/BuildLinux.js

@@ -1,106 +1,169 @@
 var fs = require('fs-extra');
 var path = require("path");
-var spawnSync = require('child_process').spawnSync
 var host = require("./Host");
-var atomicRoot = host.atomicRoot;
+var spawnSync = require('child_process').spawnSync
 
+var atomicRoot = host.atomicRoot;
 var buildDir = host.artifactsRoot + "Build/Linux/";
 var editorAppFolder = host.artifactsRoot + "AtomicEditor/";
 
-namespace('build', function() {
+var buildAtomicNET = false;
+var debug = false;
+var config = "Release";
 
-    // Builds a standalone Atomic Editor, which can be distributed out of build tree
-    task('atomiceditor', {
-        async: true
-    }, function() {
+function copyAtomicNET() {
 
-        // Clean build
-        var cleanBuild = true;
-        if (cleanBuild) {
-            common.cleanCreateDir(buildDir);
-            common.cleanCreateDir(editorAppFolder);
-            common.cleanCreateDir(host.getGenScriptRootDir("LINUX"));
-        }
+    if (!buildAtomicNET)
+        return;
 
-        var buildAtomicNET = false;
+    fs.copySync(atomicRoot + "Artifacts/AtomicNET/" + config,
+    editorAppFolder + "Resources/ToolData/AtomicNET/" + config);
 
-        // TODO: build box has old node
-        if (spawnSync)
-            buildAtomicNET = spawnSync("which", ["xbuild"]).status == 1 ? false : true;
+    fs.copySync(atomicRoot + "Script/AtomicNET/AtomicProject.json",
+    editorAppFolder + "Resources/ToolData/AtomicNET/Build/Projects/AtomicProject.json");
+
+}
+
+function copyAtomicEditor() {
+
+    // Copy the Editor binaries
+    fs.copySync(buildDir + "Source/AtomicEditor/AtomicEditor",
+    host.artifactsRoot + "AtomicEditor/AtomicEditor");
+
+    // We need some resources to run
+    fs.copySync(atomicRoot + "Resources/CoreData",
+    editorAppFolder + "Resources/CoreData");
+
+    fs.copySync(atomicRoot + "Resources/PlayerData",
+    editorAppFolder + "Resources/PlayerData");
+
+    fs.copySync(atomicRoot + "Data/AtomicEditor",
+    editorAppFolder + "Resources/ToolData");
+
+    fs.copySync(atomicRoot + "Resources/EditorData",
+    editorAppFolder + "Resources/EditorData");
+
+    fs.copySync(atomicRoot + "Artifacts/Build/Resources/EditorData/AtomicEditor/EditorScripts",
+    editorAppFolder + "Resources/EditorData/AtomicEditor/EditorScripts");
+
+    fs.copySync(buildDir +  "Source/AtomicPlayer/Application/AtomicPlayer",
+    editorAppFolder + "Resources/ToolData/Deployment/Linux/AtomicPlayer");
+
+    var binaryFiles = ["chrome-sandbox", "libcef.so", "natives_blob.bin", "snapshot_blob.bin"];
+
+    var resourceFiles = ["cef.pak",
+    "cef_100_percent.pak",
+    "cef_200_percent.pak",
+    "cef_extensions.pak",
+    "devtools_resources.pak",
+    "icudtl.dat",
+    "locales"];
+
+    for (var i = 0; i < binaryFiles.length; i++) {
+        fs.copySync(atomicRoot + "Submodules/CEF/Linux/Release/" + binaryFiles[i], editorAppFolder+"/" + binaryFiles[i]);
+    }
+
+    for (var i = 0; i < resourceFiles.length; i++) {
+        fs.copySync(atomicRoot + "Submodules/CEF/Linux/Resources/" + resourceFiles[i], editorAppFolder+"/" + resourceFiles[i]);
+    }
+
+
+    if (buildAtomicNET) {
+        copyAtomicNET();
+    }
+
+}
+
+namespace('build', function() {
+
+    task('atomiceditor_phase2', {
+        async: true
+    }, function() {
 
         process.chdir(buildDir);
 
         var cmds = [];
+        cmds.push("make AtomicEditor AtomicPlayer -j2")
 
-        cmds.push("cmake ../../../ -DATOMIC_DEV_BUILD=0 -DCMAKE_BUILD_TYPE=Release");
-        cmds.push("make -j2")
+        jake.exec(cmds, function() {
 
-        if (buildAtomicNET)
-          cmds.push(host.atomicTool + " net compile " + atomicRoot + "Script/AtomicNET/AtomicNETProject.json LINUX Release");
+            copyAtomicEditor();
 
-        jake.exec(cmds, function() {
+            complete();
 
-            // Copy the Editor binaries
-            fs.copySync(buildDir + "Source/AtomicEditor/AtomicEditor",
-            host.artifactsRoot + "AtomicEditor/AtomicEditor");
+        }, {
+            printStdout: true
+        });
 
-            // We need some resources to run
-            fs.copySync(atomicRoot + "Resources/CoreData",
-            editorAppFolder + "Resources/CoreData");
+    });
+    // Builds a standalone Atomic Editor, which can be distributed out of build tree
+    task('atomiceditor', {
+        async: true
+    }, function() {
 
-            fs.copySync(atomicRoot + "Resources/PlayerData",
-            editorAppFolder + "Resources/PlayerData");
+        var options = host.options;
 
-            fs.copySync(atomicRoot + "Data/AtomicEditor",
-            editorAppFolder + "Resources/ToolData");
+        var android = options["with-android"] ? true : false;
+        var cleanBuild = options["noclean"] ? false : true;
+        debug = options["debug"] ? true : false;
+        config = debug ? "Debug" : "Release";
 
-            fs.copySync(atomicRoot + "Resources/EditorData",
-            editorAppFolder + "Resources/EditorData");
+        var createDirs = [];
+        var removeDirs = [];
 
-            fs.copySync(atomicRoot + "Artifacts/Build/Resources/EditorData/AtomicEditor/EditorScripts",
-            editorAppFolder + "Resources/EditorData/AtomicEditor/EditorScripts");
+        // We clean atomicNET here as otherwise platform binaries would be deleted
+        createDirs.push(host.artifactsRoot + "AtomicNET/");
+        createDirs.push(buildDir);
+        createDirs.push(editorAppFolder);
+        createDirs.push(host.getGenScriptRootDir());
 
-            fs.copySync(buildDir +  "Source/AtomicPlayer/Application/AtomicPlayer",
-            editorAppFolder + "Resources/ToolData/Deployment/Linux/AtomicPlayer");
+        removeDirs.push(host.artifactsRoot + "Build/Android/");
 
-            // AtomicNET
+        host.setupDirs(cleanBuild, createDirs, removeDirs);
 
-            if (buildAtomicNET) {
+        // TODO: build box has old node
+        if (spawnSync)
+            buildAtomicNET = spawnSync("which", ["xbuild"]).status == 1 ? false : true;
 
-              fs.copySync(atomicRoot + "Artifacts/AtomicNET/Release",
-                 editorAppFolder + "Resources/ToolData/AtomicNET/Release");
+        process.chdir(buildDir);
 
-              fs.copySync(atomicRoot + "Script/AtomicNET/AtomicProject.json",
-                 editorAppFolder + "Resources/ToolData/AtomicNET/Build/Projects/AtomicProject.json");
-            }
+        var cmds = [];
 
-            var binaryFiles = ["chrome-sandbox", "libcef.so", "natives_blob.bin", "snapshot_blob.bin"];
+        // Generate Atomic solution, AtomicTool binary, and script bindings
+        cmds.push("cmake ../../../ -DATOMIC_DEV_BUILD=0 -DCMAKE_BUILD_TYPE=" + config);
+        cmds.push("make AtomicNETNative -j2")
 
-            var resourceFiles = ["cef.pak",
-            "cef_100_percent.pak",
-            "cef_200_percent.pak",
-            "cef_extensions.pak",
-            "devtools_resources.pak",
-            "icudtl.dat",
-            "locales"];
+        jake.exec(cmds, function() {
+
+            var rootTask = jake.Task['build:atomiceditor_phase2'];
+            var task = rootTask;
 
-            for (var i = 0; i < binaryFiles.length; i++) {
-                fs.copySync(atomicRoot + "Submodules/CEF/Linux/Release/" + binaryFiles[i], editorAppFolder+"/" + binaryFiles[i]);
+            // add optional build components in reverse order
+            if (buildAtomicNET) {
+                var netTask = jake.Task['build:atomicnet'];
+                task.prereqs.push("build:atomicnet")
+                task = netTask;
             }
 
-            for (var i = 0; i < resourceFiles.length; i++) {
-                fs.copySync(atomicRoot + "Submodules/CEF/Linux/Resources/" + resourceFiles[i], editorAppFolder+"/" + resourceFiles[i]);
+            if (android) {
+                var androidTask = jake.Task['build:android_native'];
+                task.prereqs.push("build:android_native")
+                task = androidTask;
             }
 
+            rootTask.addListener('complete', function () {
+                console.log("\n\nAtomic Editor built to " + editorAppFolder + "\n\n");
+                complete();
+            });
 
-            console.log("\n\nAtomic Editor build to " + editorAppFolder + "\n\n");
-
-            complete();
+            rootTask.invoke();
 
         }, {
-            printStdout: true
+            printStdout: true,
+            printStderr: true
         });
 
     });
 
-});
+
+});// end of build namespace

+ 114 - 59
Build/Scripts/BuildMac.js

@@ -2,102 +2,156 @@ var fs = require('fs-extra');
 var path = require("path");
 var spawnSync = require('child_process').spawnSync
 var host = require("./Host");
-var atomicRoot = host.atomicRoot;
 
+var atomicRoot = host.atomicRoot;
 var buildDir = host.artifactsRoot + "Build/Mac/";
 var editorAppFolder = host.artifactsRoot + "/AtomicEditor/AtomicEditor.app/";
 var resourceDest = editorAppFolder + "/Contents/Resources/"
 
-namespace('build', function() {
+var buildAtomicNET = false;
+var debug = false;
+var config = "Release";
 
-    // Builds a standalone Atomic Editor, which can be distributed out of build tree
-    task('atomiceditor', {
-        async: true
-    }, function(android) {
+function copyAtomicNET() {
 
-        android = android == "android" ? true : false;
+    if (!buildAtomicNET)
+        return;
 
-        // Clean build
-        var cleanBuild = true;
-        if (cleanBuild) {
-            common.cleanCreateDir(host.artifactsRoot + "AtomicNET/");
-            common.cleanCreateDir(buildDir);
-            common.cleanCreateDir(editorAppFolder);
-            common.cleanCreateDir(host.getGenScriptRootDir());
-        }
+    fs.copySync(atomicRoot + "Artifacts/AtomicNET/" + config,
+    resourceDest + "ToolData/AtomicNET/" + config);
 
-        var buildAtomicNET = spawnSync("which", ["xbuild"]).status == 1 ? false : true;
+    fs.copySync(atomicRoot + "Script/AtomicNET/AtomicProject.json",
+    resourceDest + "ToolData/AtomicNET/Build/Projects/AtomicProject.json");
 
-        process.chdir(buildDir);
+}
 
-        var cmds = [];
+function copyAtomicEditor() {
 
-        cmds.push("cmake ../../../ -DATOMIC_DEV_BUILD=0 -G Xcode");
-        cmds.push("xcodebuild -target AtomicEditor -target AtomicPlayer -target AtomicNETNative -configuration Release -parallelizeTargets -jobs 4")
+    fs.copySync(buildDir + "Source/AtomicEditor/" + config + "/AtomicEditor.app", editorAppFolder);
 
-        if (buildAtomicNET)
-            cmds.push(host.atomicTool + " net compile " + atomicRoot + "Script/AtomicNET/AtomicNETProject.json " + (android ? "ANDROID" : "WINDOWS") + " Release");
+    // We need some resources to run
+    fs.copySync(atomicRoot + "Resources/CoreData",
+    resourceDest + "CoreData");
 
-        function copyAtomicNET() {
+    fs.copySync(atomicRoot + "Resources/PlayerData",
+    resourceDest + "PlayerData");
 
-            if (!buildAtomicNET)
-                return;
+    fs.copySync(atomicRoot + "Data/AtomicEditor",
+    resourceDest + "ToolData");
 
-            fs.copySync(atomicRoot + "Artifacts/AtomicNET/Release",
-            resourceDest + "ToolData/AtomicNET/Release");
+    fs.copySync(atomicRoot + "Resources/EditorData",
+    resourceDest + "EditorData");
 
-            fs.copySync(atomicRoot + "Script/AtomicNET/AtomicProject.json",
-            resourceDest + "ToolData/AtomicNET/Build/Projects/AtomicProject.json");
+    fs.copySync(atomicRoot + "Artifacts/Build/Resources/EditorData/AtomicEditor/EditorScripts",
+    resourceDest + "EditorData/AtomicEditor/EditorScripts");
 
-        }
+    // copy the mac player binary to deployment
+    var playerBinary =  buildDir +  "Source/AtomicPlayer/Application/" + config + "/AtomicPlayer.app/Contents/MacOS/AtomicPlayer";
+
+    fs.copySync(playerBinary, resourceDest + "ToolData/Deployment/MacOS/AtomicPlayer.app/Contents/MacOS/AtomicPlayer");
+
+    if (buildAtomicNET) {
+        copyAtomicNET();
+    }
 
+}
+
+namespace('build', function() {
+
+    task('atomiceditor_phase2', {
+        async: true
+    }, function() {
+
+        process.chdir(buildDir);
+
+        var cmds = [];
+        cmds.push("xcodebuild -target AtomicEditor -target AtomicPlayer -configuration " + config + " -parallelizeTargets -jobs 4")
 
         jake.exec(cmds, function() {
 
-            fs.copySync(buildDir + "Source/AtomicEditor/Release/AtomicEditor.app", editorAppFolder);
+            copyAtomicEditor();
+
+            complete();
+
+        }, {
+            printStdout: true
+        });
+
+    });
 
-            // We need some resources to run
-            fs.copySync(atomicRoot + "Resources/CoreData",
-            resourceDest + "CoreData");
+    // Builds a standalone Atomic Editor, which can be distributed out of build tree
+    task('atomiceditor', {
+        async: true
+    }, function() {
 
-            fs.copySync(atomicRoot + "Resources/PlayerData",
-            resourceDest + "PlayerData");
+        var options = host.options;
 
-            fs.copySync(atomicRoot + "Data/AtomicEditor",
-            resourceDest + "ToolData");
+        var android = options["with-android"] ? true : false;
+        var ios = options["with-ios"] ? true : false;
+        var cleanBuild = options["noclean"] ? false : true;
+        debug = options["debug"] ? true : false;
+        config = debug ? "Debug" : "Release";
 
-            fs.copySync(atomicRoot + "Resources/EditorData",
-            resourceDest + "EditorData");
+        var createDirs = [];
+        var removeDirs = [];
 
-            fs.copySync(atomicRoot + "Artifacts/Build/Resources/EditorData/AtomicEditor/EditorScripts",
-            resourceDest + "EditorData/AtomicEditor/EditorScripts");
+        // We clean atomicNET here as otherwise platform binaries would be deleted
+        createDirs.push(host.artifactsRoot + "AtomicNET/");
+        createDirs.push(buildDir);
+        createDirs.push(editorAppFolder);
+        createDirs.push(host.getGenScriptRootDir());
 
-            // copy the mac player binary to deployment
-            var playerBinary =  buildDir +  "Source/AtomicPlayer/Application/Release/AtomicPlayer.app/Contents/MacOS/AtomicPlayer";
+        removeDirs.push(host.artifactsRoot + "Build/Android/");
+        removeDirs.push(host.artifactsRoot + "Build/IOS/");
 
-            fs.copySync(playerBinary, resourceDest + "ToolData/Deployment/MacOS/AtomicPlayer.app/Contents/MacOS/AtomicPlayer");
+        host.setupDirs(cleanBuild, createDirs, removeDirs);
 
-            if (android) {
+        if (!options["nonet"]) {
+            buildAtomicNET = spawnSync("which", ["xbuild"]).status == 1 ? false : true;
+        }
+
+        process.chdir(buildDir);
+
+        var cmds = [];
 
-                var androidNativeTask = jake.Task['build:android_native'];
+        // Generate XCode project, AtomicTool binary, and script bindings
+        cmds.push("cmake ../../../ -DATOMIC_DEV_BUILD=0 -G Xcode");
+        cmds.push("xcodebuild -target GenerateScriptBindings -target AtomicNETNative -configuration " + config + " -parallelizeTargets -jobs 4")
 
-                androidNativeTask.addListener('complete', function () {
-                    copyAtomicNET();
-                    console.log("\n\nAtomic Editor build to " + editorAppFolder + "\n\n");
-                    complete();
-                });
+        jake.exec(cmds, function() {
 
-                androidNativeTask.invoke();
+            var rootTask = jake.Task['build:atomiceditor_phase2'];
+            var task = rootTask;
 
+            // add optional build components in reverse order
+            if (buildAtomicNET) {
+                var netTask = jake.Task['build:atomicnet'];
+                task.prereqs.push("build:atomicnet")
+                task = netTask;
             }
-            else {
-                copyAtomicNET();
-                console.log("\n\nAtomic Editor build to " + editorAppFolder + "\n\n");
-                complete();
+
+            if (ios) {
+                var iosTask = jake.Task['build:ios_native'];
+                task.prereqs.push("build:ios_native")
+                task = iosTask;
             }
 
+            if (android) {
+                var androidTask = jake.Task['build:android_native'];
+                task.prereqs.push("build:android_native")
+                task = androidTask;
+            }
+
+            rootTask.addListener('complete', function () {
+                console.log("\n\nAtomic Editor built to " + editorAppFolder + "\n\n");
+                complete();
+            });
+
+            rootTask.invoke();
+
         }, {
-            printStdout: true
+            printStdout: true,
+            printStderr: true
         });
 
     });
@@ -124,7 +178,8 @@ namespace('build', function() {
             complete();
 
         }, {
-            printStdout: true
+            printStdout: true,
+            printStderr: true
         });
 
     });

+ 107 - 58
Build/Scripts/BuildWindows.js

@@ -1,96 +1,144 @@
 var fs = require('fs-extra');
 var path = require("path");
 var host = require("./Host");
-var atomicRoot = host.atomicRoot;
 
+var atomicRoot = host.atomicRoot;
 var buildDir = host.artifactsRoot + "Build/Windows/";
 var editorAppFolder = host.artifactsRoot + "AtomicEditor/";
 
-namespace('build', function() {
+var buildAtomicNET = true;
+var debug = false;
+var config = "Release";
 
-    // Builds a standalone Atomic Editor, which can be distributed out of build tree
-    task('atomiceditor', {
-        async: true
-    }, function(android) {
+function copyAtomicNET() {
 
-        android = android == "android" ? true : false;
+    if (!buildAtomicNET)
+        return;
 
-        // Clean build
-        var cleanBuild = true;
-        if (cleanBuild) {
-            common.cleanCreateDir(host.artifactsRoot + "AtomicNET/");
-            common.cleanCreateDir(buildDir);
-            common.cleanCreateDir(editorAppFolder);
-            common.cleanCreateDir(host.getGenScriptRootDir());
-        }
+    fs.copySync(atomicRoot + "Artifacts/AtomicNET/" + config,
+    editorAppFolder + "Resources/ToolData/AtomicNET/" + config);
 
-        process.chdir(buildDir);
+    fs.copySync(atomicRoot + "Script/AtomicNET/AtomicProject.json",
+    editorAppFolder + "Resources/ToolData/AtomicNET/Build/Projects/AtomicProject.json");
 
-        var cmds = [];
+}
 
-        // Build the AtomicEditor
-        cmds.push(atomicRoot + "Build/Scripts/Windows/CompileAtomicEditor.bat");
-        cmds.push(host.atomicTool + " net compile " + atomicRoot + "Script/AtomicNET/AtomicNETProject.json " + (android ? "ANDROID" : "WINDOWS") + " Release");
+function copyAtomicEditor() {
 
-        function copyAtomicNET() {
+    // Copy the Editor binaries
+    fs.copySync(buildDir + "Source/AtomicEditor/" + config,
+    host.artifactsRoot + "AtomicEditor");
 
-            fs.copySync(atomicRoot + "Artifacts/AtomicNET/Release",
-            editorAppFolder + "Resources/ToolData/AtomicNET/Release");
+    // We need some resources to run
+    fs.copySync(atomicRoot + "Resources/CoreData",
+    editorAppFolder + "Resources/CoreData");
 
-            fs.copySync(atomicRoot + "Script/AtomicNET/AtomicProject.json",
-            editorAppFolder + "Resources/ToolData/AtomicNET/Build/Projects/AtomicProject.json");
+    fs.copySync(atomicRoot + "Resources/PlayerData",
+    editorAppFolder + "Resources/PlayerData");
 
-        }
+    fs.copySync(atomicRoot + "Data/AtomicEditor",
+    editorAppFolder + "Resources/ToolData");
+
+    fs.copySync(atomicRoot + "Resources/EditorData",
+    editorAppFolder + "Resources/EditorData");
+
+    fs.copySync(atomicRoot + "Artifacts/Build/Resources/EditorData/AtomicEditor/EditorScripts",
+    editorAppFolder + "Resources/EditorData/AtomicEditor/EditorScripts");
+
+    fs.copySync(buildDir +  "Source/AtomicPlayer/Application/" + config +"/AtomicPlayer.exe",
+    editorAppFolder + "Resources/ToolData/Deployment/Windows/x64/AtomicPlayer.exe");
+
+    fs.copySync(buildDir +  "Source/AtomicPlayer/Application/" + config + "/D3DCompiler_47.dll",
+    editorAppFolder + "Resources/ToolData/Deployment/Windows/x64/D3DCompiler_47.dll");
+
+    if (buildAtomicNET) {
+        copyAtomicNET();
+    }
+
+}
+
+namespace('build', function() {
+
+    task('atomiceditor_phase2', {
+        async: true
+    }, function() {
+
+        process.chdir(buildDir);
+
+        var cmds = [];
+        cmds.push(atomicRoot + "Build/Scripts/Windows/CompileAtomicEditorPhase2.bat " + config);
 
         jake.exec(cmds, function() {
 
-            // Copy the Editor binaries
-            fs.copySync(buildDir + "Source/AtomicEditor/Release",
-            host.artifactsRoot + "AtomicEditor");
+            copyAtomicEditor();
 
-            // We need some resources to run
-            fs.copySync(atomicRoot + "Resources/CoreData",
-            editorAppFolder + "Resources/CoreData");
+            complete();
+
+        }, {
+            printStdout: true
+        });
 
-            fs.copySync(atomicRoot + "Resources/PlayerData",
-            editorAppFolder + "Resources/PlayerData");
+    });
+    // Builds a standalone Atomic Editor, which can be distributed out of build tree
+    task('atomiceditor', {
+        async: true
+    }, function() {
 
-            fs.copySync(atomicRoot + "Data/AtomicEditor",
-            editorAppFolder + "Resources/ToolData");
+        var options = host.options;
 
-            fs.copySync(atomicRoot + "Resources/EditorData",
-            editorAppFolder + "Resources/EditorData");
+        var android = options["with-android"] ? true : false;
+        var cleanBuild = options["noclean"] ? false : true;
+        debug = options["debug"] ? true : false;
+        config = debug ? "Debug" : "Release";
 
-            fs.copySync(atomicRoot + "Artifacts/Build/Resources/EditorData/AtomicEditor/EditorScripts",
-            editorAppFolder + "Resources/EditorData/AtomicEditor/EditorScripts");
+        var createDirs = [];
+        var removeDirs = [];
 
-            fs.copySync(buildDir +  "Source/AtomicPlayer/Application/Release/AtomicPlayer.exe",
-            editorAppFolder + "Resources/ToolData/Deployment/Windows/x64/AtomicPlayer.exe");
+        // We clean atomicNET here as otherwise platform binaries would be deleted
+        createDirs.push(host.artifactsRoot + "AtomicNET/");
+        createDirs.push(buildDir);
+        createDirs.push(editorAppFolder);
+        createDirs.push(host.getGenScriptRootDir());
 
-            fs.copySync(buildDir +  "Source/AtomicPlayer/Application/Release/D3DCompiler_47.dll",
-            editorAppFolder + "Resources/ToolData/Deployment/Windows/x64/D3DCompiler_47.dll");
+        removeDirs.push(host.artifactsRoot + "Build/Android/");
 
-            if (android) {
+        host.setupDirs(cleanBuild, createDirs, removeDirs);
 
-                var androidNativeTask = jake.Task['build:android_native'];
+        process.chdir(buildDir);
 
-                androidNativeTask.addListener('complete', function () {
-                    copyAtomicNET();
-                    console.log("\nAtomic Editor build to ", editorAppFolder);
-                    complete();
-                });
+        var cmds = [];
 
-                androidNativeTask.invoke();
+        // Generate Atomic solution, AtomicTool binary, and script bindings
+        cmds.push(atomicRoot + "Build/Scripts/Windows/CompileAtomicEditorPhase1.bat " + config);
 
+        jake.exec(cmds, function() {
+
+            var rootTask = jake.Task['build:atomiceditor_phase2'];
+            var task = rootTask;
+
+            // add optional build components in reverse order
+            if (buildAtomicNET) {
+                var netTask = jake.Task['build:atomicnet'];
+                task.prereqs.push("build:atomicnet")
+                task = netTask;
             }
-            else {
-                copyAtomicNET();
-                console.log("\nAtomic Editor build to ", editorAppFolder);
-                complete();
+
+            if (android) {
+                var androidTask = jake.Task['build:android_native'];
+                task.prereqs.push("build:android_native")
+                task = androidTask;
             }
 
+            rootTask.addListener('complete', function () {
+                console.log("\n\nAtomic Editor built to " + editorAppFolder + "\n\n");
+                complete();
+            });
+
+            rootTask.invoke();
+
         }, {
-            printStdout: true
+            printStdout: true,
+            printStderr: true
         });
 
     });
@@ -124,4 +172,5 @@ namespace('build', function() {
 
     });
 
-}); // end of build namespace
+
+});// end of build namespace

+ 2 - 0
Build/Scripts/Host.js

@@ -13,4 +13,6 @@ if (os.platform() == "win32") {
 
 require("./BuildCommon");
 require("./BuildAndroid");
+require("./BuildIOS");
 require("./BuildWeb");
+require("./BuildAtomicNET");

+ 24 - 0
Build/Scripts/HostCommon.js

@@ -138,6 +138,29 @@ function testCreateDir(directory) {
   }
 }
 
+function setupDirs(clean, createDirs, removeDirs) {
+
+    if (createDirs) {
+        for (var i = 0; i < createDirs.length; i++) {
+
+            var path = createDirs[i];
+            if (!fs.existsSync(path) || clean) {
+                cleanCreateDir(path);
+            }
+        }
+    }
+
+    if (removeDirs) {
+        for (var i = 0; i < removeDirs.length; i++) {
+
+            var path = removeDirs[i];
+            if (fs.existsSync(path) && clean) {
+                testRemoveDir(path);
+            }
+        }
+    }
+
+}
 
 function testRemoveDir(path) {
 
@@ -160,3 +183,4 @@ exports.getScriptModules = getScriptModules;
 exports.getGenScriptFilenames = getGenScriptFilenames;
 exports.createGenScriptFiles = createGenScriptFiles;
 exports.getGenScriptRootDir = getGenScriptRootDir;
+exports.setupDirs = setupDirs;

+ 235 - 0
Build/Scripts/Minimist.js

@@ -0,0 +1,235 @@
+module.exports = function (args, opts) {
+    if (!opts) opts = {};
+
+    var flags = { bools : {}, strings : {}, unknownFn: null };
+
+    if (typeof opts['unknown'] === 'function') {
+        flags.unknownFn = opts['unknown'];
+    }
+
+    if (typeof opts['boolean'] === 'boolean' && opts['boolean']) {
+      flags.allBools = true;
+    } else {
+      [].concat(opts['boolean']).filter(Boolean).forEach(function (key) {
+          flags.bools[key] = true;
+      });
+    }
+
+    var aliases = {};
+    Object.keys(opts.alias || {}).forEach(function (key) {
+        aliases[key] = [].concat(opts.alias[key]);
+        aliases[key].forEach(function (x) {
+            aliases[x] = [key].concat(aliases[key].filter(function (y) {
+                return x !== y;
+            }));
+        });
+    });
+
+    [].concat(opts.string).filter(Boolean).forEach(function (key) {
+        flags.strings[key] = true;
+        if (aliases[key]) {
+            flags.strings[aliases[key]] = true;
+        }
+     });
+
+    var defaults = opts['default'] || {};
+
+    var argv = { _ : [] };
+    Object.keys(flags.bools).forEach(function (key) {
+        setArg(key, defaults[key] === undefined ? false : defaults[key]);
+    });
+
+    var notFlags = [];
+
+    if (args.indexOf('--') !== -1) {
+        notFlags = args.slice(args.indexOf('--')+1);
+        args = args.slice(0, args.indexOf('--'));
+    }
+
+    function argDefined(key, arg) {
+        return (flags.allBools && /^--[^=]+$/.test(arg)) ||
+            flags.strings[key] || flags.bools[key] || aliases[key];
+    }
+
+    function setArg (key, val, arg) {
+        if (arg && flags.unknownFn && !argDefined(key, arg)) {
+            if (flags.unknownFn(arg) === false) return;
+        }
+
+        var value = !flags.strings[key] && isNumber(val)
+            ? Number(val) : val
+        ;
+        setKey(argv, key.split('.'), value);
+
+        (aliases[key] || []).forEach(function (x) {
+            setKey(argv, x.split('.'), value);
+        });
+    }
+
+    function setKey (obj, keys, value) {
+        var o = obj;
+        keys.slice(0,-1).forEach(function (key) {
+            if (o[key] === undefined) o[key] = {};
+            o = o[key];
+        });
+
+        var key = keys[keys.length - 1];
+        if (o[key] === undefined || flags.bools[key] || typeof o[key] === 'boolean') {
+            o[key] = value;
+        }
+        else if (Array.isArray(o[key])) {
+            o[key].push(value);
+        }
+        else {
+            o[key] = [ o[key], value ];
+        }
+    }
+
+    function aliasIsBoolean(key) {
+      return aliases[key].some(function (x) {
+          return flags.bools[x];
+      });
+    }
+
+    for (var i = 0; i < args.length; i++) {
+        var arg = args[i];
+
+        if (/^--.+=/.test(arg)) {
+            // Using [\s\S] instead of . because js doesn't support the
+            // 'dotall' regex modifier. See:
+            // http://stackoverflow.com/a/1068308/13216
+            var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
+            var key = m[1];
+            var value = m[2];
+            if (flags.bools[key]) {
+                value = value !== 'false';
+            }
+            setArg(key, value, arg);
+        }
+        else if (/^--no-.+/.test(arg)) {
+            var key = arg.match(/^--no-(.+)/)[1];
+            setArg(key, false, arg);
+        }
+        else if (/^--.+/.test(arg)) {
+            var key = arg.match(/^--(.+)/)[1];
+            var next = args[i + 1];
+            if (next !== undefined && !/^-/.test(next)
+            && !flags.bools[key]
+            && !flags.allBools
+            && (aliases[key] ? !aliasIsBoolean(key) : true)) {
+                setArg(key, next, arg);
+                i++;
+            }
+            else if (/^(true|false)$/.test(next)) {
+                setArg(key, next === 'true', arg);
+                i++;
+            }
+            else {
+                setArg(key, flags.strings[key] ? '' : true, arg);
+            }
+        }
+        else if (/^-[^-]+/.test(arg)) {
+            var letters = arg.slice(1,-1).split('');
+
+            var broken = false;
+            for (var j = 0; j < letters.length; j++) {
+                var next = arg.slice(j+2);
+
+                if (next === '-') {
+                    setArg(letters[j], next, arg)
+                    continue;
+                }
+
+                if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) {
+                    setArg(letters[j], next.split('=')[1], arg);
+                    broken = true;
+                    break;
+                }
+
+                if (/[A-Za-z]/.test(letters[j])
+                && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
+                    setArg(letters[j], next, arg);
+                    broken = true;
+                    break;
+                }
+
+                if (letters[j+1] && letters[j+1].match(/\W/)) {
+                    setArg(letters[j], arg.slice(j+2), arg);
+                    broken = true;
+                    break;
+                }
+                else {
+                    setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg);
+                }
+            }
+
+            var key = arg.slice(-1)[0];
+            if (!broken && key !== '-') {
+                if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1])
+                && !flags.bools[key]
+                && (aliases[key] ? !aliasIsBoolean(key) : true)) {
+                    setArg(key, args[i+1], arg);
+                    i++;
+                }
+                else if (args[i+1] && /true|false/.test(args[i+1])) {
+                    setArg(key, args[i+1] === 'true', arg);
+                    i++;
+                }
+                else {
+                    setArg(key, flags.strings[key] ? '' : true, arg);
+                }
+            }
+        }
+        else {
+            if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
+                argv._.push(
+                    flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
+                );
+            }
+            if (opts.stopEarly) {
+                argv._.push.apply(argv._, args.slice(i + 1));
+                break;
+            }
+        }
+    }
+
+    Object.keys(defaults).forEach(function (key) {
+        if (!hasKey(argv, key.split('.'))) {
+            setKey(argv, key.split('.'), defaults[key]);
+
+            (aliases[key] || []).forEach(function (x) {
+                setKey(argv, x.split('.'), defaults[key]);
+            });
+        }
+    });
+
+    if (opts['--']) {
+        argv['--'] = new Array();
+        notFlags.forEach(function(key) {
+            argv['--'].push(key);
+        });
+    }
+    else {
+        notFlags.forEach(function(key) {
+            argv._.push(key);
+        });
+    }
+
+    return argv;
+};
+
+function hasKey (obj, keys) {
+    var o = obj;
+    keys.slice(0,-1).forEach(function (key) {
+        o = (o[key] || {});
+    });
+
+    var key = keys[keys.length - 1];
+    return key in o;
+}
+
+function isNumber (x) {
+    if (typeof x === 'number') return true;
+    if (/^0x[0-9a-f]+$/i.test(x)) return true;
+    return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
+}

+ 1 - 1
Build/Scripts/Windows/CompileAndroid.bat

@@ -1,3 +1,3 @@
 SET PATH=%ANDROID_NDK%\prebuilt\windows\bin\;%PATH%
-cmake -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=../../../Build/CMake/Toolchains/android.toolchain.cmake -DCMAKE_BUILD_TYPE=Release ../../../
+cmake -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=../../../Build/CMake/Toolchains/android.toolchain.cmake -DCMAKE_BUILD_TYPE=%1 ../../../
 %ANDROID_NDK%\prebuilt\windows\bin\make.exe -j4

+ 3 - 0
Build/Scripts/Windows/CompileAtomicEditorPhase1.bat

@@ -0,0 +1,3 @@
+call "%VS140COMNTOOLS%..\..\VC\bin\amd64\vcvars64.bat"
+cmake ..\\..\\..\\ -DATOMIC_DEV_BUILD=0 -G "Visual Studio 14 2015 Win64"
+msbuild /m Atomic.sln /t:AtomicNETNative /p:Configuration=%1 /p:Platform=x64

+ 2 - 0
Build/Scripts/Windows/CompileAtomicEditorPhase2.bat

@@ -0,0 +1,2 @@
+call "%VS140COMNTOOLS%..\..\VC\bin\amd64\vcvars64.bat"
+msbuild /m Atomic.sln /t:AtomicEditor /t:AtomicPlayer /p:Configuration=%1 /p:Platform=x64

+ 1 - 36
Build_AtomicEditor.bat

@@ -1,37 +1,2 @@
 @echo OFF
-setlocal enabledelayedexpansion
-
-set ATOMICEDITOR_BUILD_CMD=Build\Windows\node\node.exe Build\node_modules\jake\bin\cli.js -f ./Build/Scripts/Bootstrap.js build:atomiceditor
-
-for %%a in (%*) do (
-  if /I "%%a"=="--with-android" (set ATOMICEDITOR_ANDROID=YES)
-)
-
-:: If we're building in android support, make sure ANDROID_NDK is defined
-if "%ATOMICEDITOR_ANDROID%" == "YES" (
-
-    if "%ANDROID_NDK%" == "" (
-        @echo:
-        echo ANDROID_NDK not set, exiting
-        @echo:
-        exit /B
-    )
-
-    @echo:
-    echo Building Atomic Editor with Android support
-    @echo:
-    set ATOMICEDITOR_BUILD_CMD=!ATOMICEDITOR_BUILD_CMD![android]
-)
-
-@echo:
-@echo:
-ECHO Building Atomic Editor, this process will take a few minutes
-@echo:
-@echo:
-PAUSE
-%ATOMICEDITOR_BUILD_CMD%
-@echo:
-@echo:
-PAUSE
-
-endlocal
+Build\Windows\node\node.exe ./Build/Scripts/Bootstrap.js buildeditor %*

+ 3 - 17
Build_AtomicEditor.sh

@@ -1,23 +1,9 @@
 #!/usr/bin/env sh
 
-buildcmd="build:atomiceditor"
-
-for var in "$@"
-do
-    if [ $var == "--with-android" ]; then
-        if [ "$ANDROID_NDK" == "" ]; then
-            echo "\n\nANDROID_NDK environment variable not set, exiting\n\n"
-            exit 1
-        fi
-        echo "\n\nBuilding Atomic Editor with Android support\n\n"
-        buildcmd+="[android]"
-    fi
-done
-
 if [ "$(uname)" = "Darwin" ]; then
-    ./Build/Mac/node/node ./Build/node_modules/jake/bin/cli.js -f ./Build/Scripts/Bootstrap.js $buildcmd
+    ./Build/Mac/node/node ./Build/Scripts/Bootstrap.js buildeditor "$@"
 elif [ "$(expr substr $(uname -s) 1 5)" = "Linux" ]; then
-    ./Build/Linux/node/node ./Build/node_modules/jake/bin/cli.js -f ./Build/Scripts/Bootstrap.js $buildcmd
+    ./Build/Linux/node/node ./Build/Scripts/Bootstrap.js buildeditor "$@"
 elif [ "$(expr substr $(uname -s) 1 7)" = "MSYS_NT" ]; then
-    ./Build/Windows/node/node.exe ./Build/node_modules/jake/bin/cli.js -f ./Build/Scripts/Bootstrap.js $buildcmd
+    ./Build/Windows/node/node.exe ./Build/Scripts/Bootstrap.js buildeditor "$@"
 fi

+ 8 - 1
Script/AtomicNET/AtomicNET/Application/Application.cs

@@ -28,7 +28,14 @@ namespace AtomicEngine
             app = NETIPCPlayerApp.Create(args);
 #endif
 
-#if ATOMIC_ANDROID
+#if ATOMIC_IOS
+            // On iOS, we need to set main ready as main() isn't being called
+            SDLEvents.SetMainReady();
+#endif
+
+
+#if ATOMIC_ANDROID || ATOMIC_IOS
+
             app = NETAtomicPlayer.Create(args);
             
             var renderer = AtomicNET.GetSubsystem<Renderer>();

+ 2 - 7
Script/AtomicNET/AtomicNET/Core/AtomicNET.cs

@@ -77,11 +77,7 @@ namespace AtomicEngine
 
             PlayerModule.Initialize();
 
-            coreDelegates = new CoreDelegates();
-            coreDelegates.eventDispatch = NativeCore.EventDispatch;
-            coreDelegates.updateDispatch = NativeCore.UpdateDispatch;
-
-            IntPtr coreptr = csi_Atomic_NETCore_Initialize(ref coreDelegates);
+            IntPtr coreptr = csi_Atomic_NETCore_Initialize(NativeCore.EventDispatch, NativeCore.UpdateDispatch);
 
             NETCore core = (coreptr == IntPtr.Zero ? null : NativeCore.WrapNative<NETCore>(coreptr));
 
@@ -102,10 +98,9 @@ namespace AtomicEngine
         }
 
         [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
-        private static extern IntPtr csi_Atomic_NETCore_Initialize(ref CoreDelegates delegates);
+        private static extern IntPtr csi_Atomic_NETCore_Initialize(EventDispatchDelegate eventDispatch, UpdateDispatchDelegate updateDispatch);
 
         private static Context context;
-        private static CoreDelegates coreDelegates;
         private static Dictionary<Type, AObject> subSystems = new Dictionary<Type, AObject>();
 
     }

+ 3 - 1
Script/AtomicNET/AtomicNET/Core/Constants.cs

@@ -4,7 +4,9 @@ namespace AtomicEngine
     public static partial class Constants
     {
 
-#if __MonoCS__
+#if ATOMIC_IOS
+        public const string LIBNAME = "@rpath/AtomicNETNative.framework/AtomicNETNative";
+#elif __MonoCS__
         public const string LIBNAME = "AtomicNETNative";
 #else
         public const string LIBNAME = "AtomicNETNative.dll";

+ 30 - 13
Script/AtomicNET/AtomicNET/Core/NativeCore.cs

@@ -5,9 +5,19 @@ using System.Runtime.InteropServices;
 using System.Linq;
 using static System.Reflection.IntrospectionExtensions;
 
+#if ATOMIC_IOS
+using ObjCRuntime;
+#endif
+
 namespace AtomicEngine
 {
 
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate void EventDispatchDelegate(IntPtr sender, uint eventType, IntPtr eventData);
+
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate void UpdateDispatchDelegate(float timeStep);
+
     public class NativeType
     {
 
@@ -124,6 +134,26 @@ namespace AtomicEngine
                 svm[i] = new ScriptVariantMap();
         }
 
+
+        static float expireDelta = 0.0f;
+
+        // called ahead of E_UPDATE event
+#if ATOMIC_IOS
+        [MonoPInvokeCallback(typeof(UpdateDispatchDelegate))]
+#endif
+        public static void UpdateDispatch(float timeStep)
+        {
+            expireDelta += timeStep;
+            if (expireDelta > 2.0f)
+            {
+                expireDelta = 0.0f;
+                ExpireNatives();
+            }
+        }
+
+        #if ATOMIC_IOS
+        [MonoPInvokeCallback(typeof(EventDispatchDelegate))]
+        #endif
         public static void EventDispatch(IntPtr sender, uint eventType, IntPtr eventData)
         {
             List<EventSubscription> eventReceivers;
@@ -240,19 +270,6 @@ namespace AtomicEngine
 
         }
 
-        static float expireDelta = 0.0f;
-
-        // called ahead of E_UPDATE event
-        public static void UpdateDispatch(float timeStep)
-        {
-            expireDelta += timeStep;
-            if (expireDelta > 2.0f)
-            {
-                expireDelta = 0.0f;
-                ExpireNatives();
-            }
-        }
-
         // register a newly created native
         public static IntPtr RegisterNative(IntPtr native, RefCounted r)
         {

+ 0 - 18
Script/AtomicNET/AtomicNET/Core/NativeEvents.cs

@@ -3,28 +3,10 @@ using System.Runtime.InteropServices;
 
 namespace AtomicEngine
 {
-    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
-    public delegate void EventDispatchDelegate(IntPtr sender, uint eventType, IntPtr eventData);
-
-    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
-    public delegate void UpdateDispatchDelegate(float timeStep);
-
     public delegate void EventDelegate(uint eventType, ScriptVariantMap eventData);
 
     public delegate void SenderEventDelegate(AObject sender, uint eventType, ScriptVariantMap eventData);
 
-    public delegate void HandleUpdateDelegate(float timeStep);
-
-    [StructLayout(LayoutKind.Sequential)]
-    public struct CoreDelegates
-    {
-        [MarshalAs(UnmanagedType.FunctionPtr)]
-        public EventDispatchDelegate eventDispatch;
-
-        [MarshalAs(UnmanagedType.FunctionPtr)]
-        public UpdateDispatchDelegate updateDispatch;
-    }
-
     public partial class ScriptVariantMap
     {
         public void CopyVariantMap(IntPtr vm)

+ 144 - 0
Script/AtomicNET/AtomicNET/Core/SDLEvents.cs

@@ -0,0 +1,144 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace AtomicEngine
+{
+	public static class SDLEvents
+	{
+		[DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl)]
+		static extern int SDL_SendWindowEvent(IntPtr window, byte windowevent, int data1, int data2);
+
+		[DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl)]
+		static extern int SDL_SendAppEvent(byte eventType);
+
+		[DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl)]
+		static extern void SDL_SetMainReady();
+
+		public static void SetMainReady() => SDL_SetMainReady();
+
+		public static void SendWindowEvent(SdlWindowEvent wndEvent, int data1 = 0, int data2 = 0)
+		{
+            var graphics = AtomicNET.GetSubsystem<Graphics>();
+			SDL_SendWindowEvent(graphics.SDLWindow, (byte) wndEvent, data1, data2);
+		}
+
+		public static void SendAppEvent(SdlEventType eventType) => SDL_SendAppEvent((byte)eventType);
+	}
+
+	//see SDL_Video.h
+	public enum SdlWindowEvent
+	{
+		SDL_WINDOWEVENT_NONE,           /* Never used */
+		SDL_WINDOWEVENT_SHOWN,          /* Window has been shown */
+		SDL_WINDOWEVENT_HIDDEN,         /* Window has been hidden */
+		SDL_WINDOWEVENT_EXPOSED,        /* Window has been exposed and should be redrawn */
+		SDL_WINDOWEVENT_MOVED,          /* Window has been moved to data1, data2 */
+		SDL_WINDOWEVENT_RESIZED,        /* Window has been resized to data1xdata2 */
+		SDL_WINDOWEVENT_SIZE_CHANGED,   /* The window size has changed, either as a result of an API call or through the system or user changing the window size. */
+		SDL_WINDOWEVENT_MINIMIZED,      /* Window has been minimized */
+		SDL_WINDOWEVENT_MAXIMIZED,      /* Window has been maximized */
+		SDL_WINDOWEVENT_RESTORED,       /* Window has been restored to normal size and position */
+		SDL_WINDOWEVENT_ENTER,          /* Window has gained mouse focus */
+		SDL_WINDOWEVENT_LEAVE,          /* Window has lost mouse focus */
+		SDL_WINDOWEVENT_FOCUS_GAINED,   /* Window has gained keyboard focus */
+		SDL_WINDOWEVENT_FOCUS_LOST,     /* Window has lost keyboard focus */
+		SDL_WINDOWEVENT_CLOSE           /* The window manager requests that the window be closed */
+	}
+
+	//see SDL_Events.h
+	public enum SdlEventType
+	{
+		SDL_FIRSTEVENT = 0,     /**< Unused (do not remove) */
+
+		/* Application events */
+		SDL_QUIT = 0x100, /**< User-requested quit */
+
+		/* These application events have special meaning on iOS, see README-ios.txt for details */
+		SDL_APP_TERMINATING,        /**< The application is being terminated by the OS
+									 Called on iOS in applicationWillTerminate()
+									 Called on Android in onDestroy()
+								*/
+		SDL_APP_LOWMEMORY,          /**< The application is low on memory, free memory if possible.
+									 Called on iOS in applicationDidReceiveMemoryWarning()
+									 Called on Android in onLowMemory()
+								*/
+		SDL_APP_WILLENTERBACKGROUND, /**< The application is about to enter the background
+									 Called on iOS in applicationWillResignActive()
+									 Called on Android in onPause()
+								*/
+		SDL_APP_DIDENTERBACKGROUND, /**< The application did enter the background and may not get CPU for some time
+									 Called on iOS in applicationDidEnterBackground()
+									 Called on Android in onPause()
+								*/
+		SDL_APP_WILLENTERFOREGROUND, /**< The application is about to enter the foreground
+									 Called on iOS in applicationWillEnterForeground()
+									 Called on Android in onResume()
+								*/
+		SDL_APP_DIDENTERFOREGROUND, /**< The application is now interactive
+									 Called on iOS in applicationDidBecomeActive()
+									 Called on Android in onResume()
+								*/
+
+		/* Window events */
+		SDL_WINDOWEVENT = 0x200, /**< Window state change */
+		SDL_SYSWMEVENT,             /**< System specific event */
+
+		/* Keyboard events */
+		SDL_KEYDOWN = 0x300, /**< Key pressed */
+		SDL_KEYUP,                  /**< Key released */
+		SDL_TEXTEDITING,            /**< Keyboard text editing (composition) */
+		SDL_TEXTINPUT,              /**< Keyboard text input */
+
+		/* Mouse events */
+		SDL_MOUSEMOTION = 0x400, /**< Mouse moved */
+		SDL_MOUSEBUTTONDOWN,        /**< Mouse button pressed */
+		SDL_MOUSEBUTTONUP,          /**< Mouse button released */
+		SDL_MOUSEWHEEL,             /**< Mouse wheel motion */
+
+		/* Joystick events */
+		SDL_JOYAXISMOTION = 0x600, /**< Joystick axis motion */
+		SDL_JOYBALLMOTION,          /**< Joystick trackball motion */
+		SDL_JOYHATMOTION,           /**< Joystick hat position change */
+		SDL_JOYBUTTONDOWN,          /**< Joystick button pressed */
+		SDL_JOYBUTTONUP,            /**< Joystick button released */
+		SDL_JOYDEVICEADDED,         /**< A new joystick has been inserted into the system */
+		SDL_JOYDEVICEREMOVED,       /**< An opened joystick has been removed */
+
+		/* Game controller events */
+		SDL_CONTROLLERAXISMOTION = 0x650, /**< Game controller axis motion */
+		SDL_CONTROLLERBUTTONDOWN,          /**< Game controller button pressed */
+		SDL_CONTROLLERBUTTONUP,            /**< Game controller button released */
+		SDL_CONTROLLERDEVICEADDED,         /**< A new Game controller has been inserted into the system */
+		SDL_CONTROLLERDEVICEREMOVED,       /**< An opened Game controller has been removed */
+		SDL_CONTROLLERDEVICEREMAPPED,      /**< The controller mapping was updated */
+
+		/* Touch events */
+		SDL_FINGERDOWN = 0x700,
+		SDL_FINGERUP,
+		SDL_FINGERMOTION,
+
+		/* Gesture events */
+		SDL_DOLLARGESTURE = 0x800,
+		SDL_DOLLARRECORD,
+		SDL_MULTIGESTURE,
+
+		/* Clipboard events */
+		SDL_CLIPBOARDUPDATE = 0x900, /**< The clipboard changed */
+
+		/* Drag and drop events */
+		SDL_DROPFILE = 0x1000, /**< The system requests a file open */
+
+		/* Render events */
+		SDL_RENDER_TARGETS_RESET = 0x2000, /**< The render targets have been reset */
+
+		/** Events ::SDL_USEREVENT through ::SDL_LASTEVENT are for your use,
+		 *  and should be allocated with SDL_RegisterEvents()
+		 */
+		SDL_USEREVENT = 0x8000,
+
+		/**
+		 *  This last event is only for bounding internal arrays
+		 */
+		SDL_LASTEVENT = 0xFFFF
+	}
+}

+ 8 - 0
Script/AtomicNET/AtomicNET/Graphics/Graphics.cs

@@ -8,6 +8,14 @@ namespace AtomicEngine
     public partial class Graphics : AObject
     {
 
+        [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl)]
+        static extern IntPtr csi_Atomic_Graphics_GetSDLWindow();
+
+        /// <summary>
+        /// Pointer to SDL window
+        /// </summary>
+        public IntPtr SDLWindow => csi_Atomic_Graphics_GetSDLWindow();
+
         // Shader Parameters
 
         public void SetShaderParameter(string param, Matrix3x4 matrix)

+ 26 - 0
Script/AtomicNET/AtomicNETProject.json

@@ -101,6 +101,32 @@
                 "$ATOMIC_ROOT$/Script/AtomicNET/Platform/Android/"
             ]
         },
+        {
+            "name": "AtomicNET.iOS",
+            "platforms" : ["ios"],
+            "outputType" : "Library",
+            "projectTypeGuids" :[ "8FFB629D-F513-41CE-95D2-7ECE97B6EEEC", "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC" ],
+            "defineConstants" : ["ATOMIC_IOS"],
+            "rootNamespace" : "AtomicGameEngine",
+            "assemblyName" : "AtomicNET",
+            "assemblyOutputPath" : "..\\..\\$ATOMIC_CONFIG$\\iOS\\",
+            "projectGuid" : "C74AB5E8-7517-11E6-AE3F-005056C00008",
+            "objcBindingApiDefinition" : "$ATOMIC_ROOT$/Script/AtomicNET/Platform/iOS/ApiDefinition/ApiDefinition.cs",
+            "references" : [
+                "Xamarin.iOS",
+                "System",
+                "System.Core",
+                "System.Xml.Linq",
+                "System.Xml"
+            ],
+            "sharedReferences" : [
+                "AtomicNET.Shared"
+            ],
+            "sources" : [
+                "$ATOMIC_ROOT$/Script/AtomicNET/Platform/iOS/AtomicNET"
+            ],
+            "importProjects" : ["$(MSBuildExtensionsPath)\\Xamarin\\iOS\\Xamarin.iOS.ObjCBinding.CSharp.targets"]
+        },
         {
             "name": "AtomicNETService",
             "platforms" : ["desktop"],

+ 0 - 7
Script/AtomicNET/AtomicNETService/Program.cs

@@ -1,11 +1,6 @@
 using System;
 using AtomicEngine;
 
-// net genproject C:\Dev\atomic\AtomicGameEngine\Script\AtomicNET\AtomicNETProject.json WINDOWS
-// net parse C:\Dev\atomic\AtomicGameEngine\Artifacts\AtomicNET\Build\AtomicNETService\bin\Debug\AtomicNETService.exe
-// net compile C:\Dev\atomic\AtomicGameEngine\Artifacts\AtomicNET\Build\AtomicNET.sln
-
-
 namespace AtomicTools
 {
 
@@ -68,5 +63,3 @@ namespace AtomicTools
         }
     }
 }
-
-

+ 31 - 0
Script/AtomicNET/AtomicProject.json

@@ -84,6 +84,37 @@
                 "$ATOMIC_ROOT$/Script/AtomicNET/AtomicPlayer/AtomicPlayer.Common",
                 "$ATOMIC_PROJECT_ROOT$/Project/AtomicNET/Platforms/Android"
             ]
+        },
+        {
+            "name": "$ATOMIC_PROJECT_NAME$.iOS",
+            "platforms" : ["ios"],
+            "outputType" : "Exe",
+            "defineConstants" : ["ATOMIC_IOS"],
+            "rootNamespace" : "",
+            "assemblyName" : "$ATOMIC_PROJECT_NAME$",
+            "projectGuid" : "071BD84E-7518-11E6-C78E-005056C00008",
+            "projectTypeGuids" : ["FEACFBD2-3405-455C-9665-78FE426C6842", "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC"],
+            "assemblyOutputPath" : "$ATOMIC_PROJECT_ROOT$\\AtomicNET\\$ATOMIC_CONFIG$\\Bin\\iOS",
+            "playerApplication" : true,
+            "codesignEntitlements" : "$ATOMIC_PROJECT_ROOT$/Project/AtomicNET/Platforms/iOS/Entitlements.plist",
+            "infoPList" : "$ATOMIC_PROJECT_ROOT$/Project/AtomicNET/Platforms/iOS/Info.plist",
+            "references" : [
+                "Xamarin.iOS",
+                "System",
+                "System.Core",
+                "System.Xml.Linq",
+                "System.Xml",
+                "AtomicNET.iOS"
+            ],
+            "sharedReferences" : [
+                "$ATOMIC_PROJECT_NAME$.Shared"
+            ],
+            "sources" : [
+                "$ATOMIC_ROOT$/Script/AtomicNET/AtomicPlayer/AtomicPlayer.Common",
+                "$ATOMIC_PROJECT_ROOT$/Project/AtomicNET/Platforms/iOS"
+            ],
+            "importProjects" : ["$(MSBuildExtensionsPath)\\Xamarin\\iOS\\Xamarin.iOS.CSharp.targets"]
         }
+
     ]
 }

+ 10 - 0
Script/AtomicNET/Platform/iOS/ApiDefinition/ApiDefinition.cs

@@ -0,0 +1,10 @@
+using System;
+
+using UIKit;
+using Foundation;
+using ObjCRuntime;
+using CoreGraphics;
+
+namespace AtomicGameEngine
+{
+}

+ 3 - 0
Script/AtomicNET/Platform/iOS/AtomicNET/AtomicNETNative.framework.linkwith.cs

@@ -0,0 +1,3 @@
+using ObjCRuntime;
+
+[assembly: LinkWith ("AtomicNETNative.framework")]

+ 29 - 0
Source/AtomicNET/NETNative/CMakeLists.txt

@@ -5,6 +5,12 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}
                     ${CMAKE_SOURCE_DIR}/Source/ThirdParty/FreeType/include
                     ${CMAKE_SOURCE_DIR}/Source/ThirdParty/Box2D)
 
+# TODO: Look into the application-extension flag for iOS
+#if (IOS)
+#    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fapplication-extension")
+#    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fapplication-extension")
+#endif()
+
 set (CSATOMICDIR "${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/CSharp/Packages/")
 
 file (GLOB CSHARP_BINDINGS_SOURCE ${CSATOMICDIR}/Atomic/Native/*.cpp ${CSATOMICDIR}/Atomic/Native/*.h
@@ -44,6 +50,18 @@ if (APPLE)
 
         target_link_libraries( AtomicNETNative "-stdlib=libc++ -framework AudioUnit -framework Carbon -framework Cocoa -framework CoreAudio -framework CoreVideo -framework ForceFeedback -framework IOKit -framework OpenGL -framework CoreServices -framework Security")
 
+    else()
+
+        set_target_properties(AtomicNETNative PROPERTIES
+          FRAMEWORK TRUE
+          MACOSX_FRAMEWORK_IDENTIFIER com.atomicgameengine.atomicnetframework
+          MACOSX_FRAMEWORK_INFO_PLIST ${CMAKE_SOURCE_DIR}/Build/CMake/IOS/AtomicNET.framework.plist
+          # PUBLIC_HEADER dynamicFramework.h
+          XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer"
+        )
+
+        target_link_libraries( AtomicNETNative "-framework AudioToolbox -framework CoreAudio -framework CoreGraphics -framework Foundation -framework OpenGLES -framework QuartzCore -framework UIKit -framework CoreMotion -framework GameController")
+
     endif()
 
 endif()
@@ -64,10 +82,21 @@ endif()
 
 set (ATOMICNET_NATIVE_DIR "${CMAKE_SOURCE_DIR}/Artifacts/AtomicNET/$<$<CONFIG:debug>:Debug>$<$<CONFIG:release>:Release>/Native/${ATOMICNET_NATIVE_PLATFORM}")
 
+if (NOT IOS)
+
 add_custom_command( TARGET AtomicNETNative POST_BUILD
                     COMMAND "${CMAKE_COMMAND}" ARGS -E make_directory "\"${ATOMICNET_NATIVE_DIR}\""
                     COMMAND "${CMAKE_COMMAND}" ARGS -E copy_if_different \"$<TARGET_FILE:AtomicNETNative>\" "\"${ATOMICNET_NATIVE_DIR}\"" )
 
+else()
+
+    # iOS: We need to copy/zip the framework on iOS, the framework must also be code signed and POST_BUILD is triggered before this occurs,
+    # so handled in BuildIOS.js
+    add_custom_command( TARGET AtomicNETNative POST_BUILD
+                        COMMAND install_name_tool -id @rpath/AtomicNETNative.framework/AtomicNETNative \"$<TARGET_FILE:AtomicNETNative>\")
+
+endif()
+
 if (MSVC)
 
     # Copy the D3D shader compiler (for pre-Windows 8)

+ 8 - 6
Source/AtomicNET/NETNative/NETAtomicPlayer.cpp

@@ -38,7 +38,7 @@
 #include <stdio.h>
 #endif
 
-// Android or iOS: use SDL_main
+// Android: uses SDL_main, defined for IOS only to satifsy linker
 #if defined(__ANDROID__) || defined(IOS)
 
 typedef int(*sdl_entry_callback)();
@@ -50,14 +50,16 @@ extern "C" void RegisterSDLEntryCallback(sdl_entry_callback callback)
 	sdlEntryCallback = callback;
 }
 
-extern "C" int SDL_main(int argc, char** argv); 
-int SDL_main(int argc, char** argv) 
-{ 
-    Atomic::ParseArguments(argc, argv); 
+extern "C" int SDL_main(int argc, char** argv);
+int SDL_main(int argc, char** argv)
+{
+#if defined(__ANDROID__)
+    Atomic::ParseArguments(argc, argv);
 	if (sdlEntryCallback != 0)
 	{
 		return sdlEntryCallback();
 	}
+#endif	
 	return 0;
 }
 #endif
@@ -75,7 +77,7 @@ namespace Atomic
     {
         PlayerApp::Setup();
 
-		engineParameters_["ResourcePaths"] = "AtomicResources"; 
+		engineParameters_["ResourcePaths"] = "AtomicResources";
     }
 
     int NETAtomicPlayer::Initialize()

+ 53 - 2
Source/AtomicNET/NETNative/NETCInterop.cpp

@@ -22,6 +22,11 @@
 namespace Atomic
 {
 
+#ifdef ATOMIC_PLATFORM_IOS
+    static const char *sdlResourceDir = 0;
+    static const char *sdlDocumentsDir = 0;
+#endif
+
     extern "C"
     {
 
@@ -60,10 +65,16 @@ namespace Atomic
             obj->SendEvent(eventType, vmap ? vmap->GetVariantMap() : obj->GetEventDataMap());
         }
 
-        ATOMIC_EXPORT_API ClassID csi_Atomic_NETCore_Initialize(NETCoreDelegates* delegates)
+        ATOMIC_EXPORT_API ClassID csi_Atomic_NETCore_Initialize(NETCoreEventDispatchFunction eventDispatch, NETCoreUpdateDispatchFunction updateDispatch)
         {
             Context* context = new Context();
-            NETCore* netCore = new NETCore(context, delegates);
+
+            // NOTE: We don't simply marshal the NETCoreDelegates structure due to iOS "reverse callback" limitation
+            NETCoreDelegates delegates;
+            delegates.eventDispatch = eventDispatch;
+            delegates.updateDispatch = updateDispatch;
+
+            NETCore* netCore = new NETCore(context, &delegates);
             context->RegisterSubsystem(netCore);
             return netCore;
         }
@@ -123,6 +134,20 @@ namespace Atomic
 
         }
 
+        // Graphics
+
+        ATOMIC_EXPORT_API void* csi_Atomic_Graphics_GetSDLWindow()
+        {
+            if (!NETCore::GetContext())
+                return 0;
+
+            if (!NETCore::GetContext()->GetSubsystem<Graphics>())
+                return 0;
+
+            return NETCore::GetContext()->GetSubsystem<Graphics>()->GetSDLWindow();
+
+        }
+
         ATOMIC_EXPORT_API void* csi_Atomic_VertexBuffer_Lock(VertexBuffer* vb , unsigned start, unsigned count, bool discard)
         {
             if (!vb)
@@ -140,9 +165,35 @@ namespace Atomic
             graphics->SetShaderParameter(param, *matrix);
         }
 
+#ifdef ATOMIC_PLATFORM_IOS
+        ATOMIC_EXPORT_API void SDL_IOS_Init(const char *resourceDir, const char *documentsDir)
+        {
+            sdlResourceDir = resourceDir;
+            sdlDocumentsDir = documentsDir;
+        }
+#endif
+
 
     }
 }
 
+#ifdef ATOMIC_PLATFORM_IOS
+
+//FileSystem.cpp uses these functions as external.
+const char* SDL_IOS_GetResourceDir()
+{
+    return Atomic::sdlResourceDir;
+}
+
+const char* SDL_IOS_GetDocumentsDir()
+{
+    return Atomic::sdlDocumentsDir;
+}
+
+#endif
+
+
+
+
 
 

+ 1 - 1
Source/AtomicPlayer/Application/CMakeLists.txt

@@ -55,7 +55,7 @@ if (APPLE)
       XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer"
       XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf-with-dsym"
       XCODE_ATTRIBUTE_INFOPLIST_PREPROCESS YES
-      XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET 9.2
+      XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET 9.3
       XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2" # iPhone/iPad
       XCODE_ATTRIBUTE_COMBINE_HIDPI_IMAGES NO
     )

+ 1 - 1
Source/CMakeLists.txt

@@ -12,7 +12,7 @@ endif()
 add_subdirectory(AtomicPlayer)
 add_subdirectory(AtomicPlayerJS)
 
-if (NOT IOS AND NOT EMSCRIPTEN)
+if (NOT EMSCRIPTEN)
     add_subdirectory(AtomicApp)
     add_subdirectory(AtomicNET)
 endif()

+ 43 - 9
Source/ToolCore/Command/NETCmd.cpp

@@ -67,11 +67,34 @@ bool NETCmd::Parse(const Vector<String>& arguments, unsigned startIndex, String&
         return false;
     }
 
+    if (command_ == "compile" || command_ == "genresources")
+    {
+        for (unsigned i = startIndex + 3; i < arguments.Size(); i++)
+        {
+            if (arguments[i].Length() > 1 && arguments[i][0] == '-')
+            {
+                String argument = arguments[i].Substring(1).ToLower();
+                String value = i + 1 < arguments.Size() ? arguments[i + 1] : String::EMPTY;
+
+                if (argument == "platform" && !value.Empty())
+                {
+                    platforms_.Push(value);
+                    i++;
+                }
+                else if (argument == "config" && !value.Empty())
+                {
+                    configurations_.Push(value);
+                    i++;
+                }
+            }
+        }
+    }
+
     if (command_ == "compile")
     {
+        // solution
         solutionPath_ = startIndex + 2 < arguments.Size() ? arguments[startIndex + 2] : String::EMPTY;
-        platform_ = startIndex + 3 < arguments.Size() ? arguments[startIndex + 3] : String::EMPTY;
-        configuration_ = startIndex + 4 < arguments.Size() ? arguments[startIndex + 4] : "Release";
+
 
         bool exists = false;
 
@@ -87,19 +110,32 @@ bool NETCmd::Parse(const Vector<String>& arguments, unsigned startIndex, String&
             return false;
         }
 
-        if (!platform_.Length())
+        if (!platforms_.Size())
         {
             errorMsg = "Platform not specified";
             return false;
         }
 
+        if (!configurations_.Size())
+        {
+            errorMsg = "configuration not specified";
+            return false;
+        }
+
+
         return true;
     }
 	else if (command_ == "genresources")
 	{
 		projectPath_ = startIndex + 2 < arguments.Size() ? arguments[startIndex + 2] : String::EMPTY;
-		platform_ = startIndex + 3 < arguments.Size() ? arguments[startIndex + 3] : String::EMPTY;
 		requiresProjectLoad_ = true;
+
+        if (!platforms_.Size())
+        {
+            errorMsg = "Platform not specified";
+            return false;
+        }
+
 	}
     else
     {
@@ -136,8 +172,6 @@ void NETCmd::Run()
     if (command_ == "compile")
     {
 		
-		FileSystem* fileSystem = GetSubsystem<FileSystem>();
-
         NETBuildSystem* buildSystem = new NETBuildSystem(context_);
         context_->RegisterSubsystem(buildSystem);
 
@@ -173,7 +207,7 @@ void NETCmd::Run()
 		}
 
 		// json project file
-		build = buildSystem->Build(solutionPath_, platform_, configuration_);
+        build = buildSystem->Build(solutionPath_, platforms_, configurations_);
 
         if (!build)
         {
@@ -200,11 +234,11 @@ void NETCmd::Run()
 
 		buildSystem->SetBuildPath(project->GetProjectPath() + "AtomicNET/Resources/");
 
-		Platform* platform = toolSystem->GetPlatformByName(platform_);
+        Platform* platform = toolSystem->GetPlatformByName(platforms_[0]);
 
 		if (!platform)
 		{
-			Error(ToString("Unknown platform %s", platform_.CString()));
+            Error(ToString("Unknown platform %s", platforms_[0].CString()));
 			Finished();
 			return;
 		}

+ 2 - 2
Source/ToolCore/Command/NETCmd.h

@@ -64,8 +64,8 @@ private:
 
     // compile
     String solutionPath_;
-    String platform_;
-    String configuration_;
+    StringVector platforms_;
+    StringVector configurations_;
 
 	bool requiresProjectLoad_;
 

+ 76 - 32
Source/ToolCore/NETTools/NETBuildSystem.cpp

@@ -41,14 +41,23 @@
 namespace ToolCore
 {
 
-    NETBuild::NETBuild(Context* context, const String& solutionPath, const String& platform, const String& configuration) :
+    NETBuild::NETBuild(Context* context, const String& solutionPath, const StringVector& platforms, const StringVector& configurations) :
         Object(context),
         solutionPath_(solutionPath),
-        platform_(platform),
-        configuration_(configuration),
         status_(NETBUILD_PENDING)
     {
+        for (unsigned i = 0; i < platforms.Size() ; i++)
+        {
+            platforms_.Push(platforms[i].ToLower());
+        }
 
+        for (unsigned i = 0; i < configurations.Size() ; i++)
+        {
+            String config = configurations[i];
+            config.Replace("release", "Release");
+            config.Replace("debug", "Debug");
+            configurations_.Push(config);
+        }
     }
 
     NETBuildSystem::NETBuildSystem(Context* context) :
@@ -187,15 +196,8 @@ namespace ToolCore
             {
                 SharedPtr<NETProjectGen> gen(new NETProjectGen(context_));
 
-				StringVector platforms;
-				platforms.Push("desktop");
-
-				if (curBuild_->platform_.ToLower() == "android")
-				{
-					platforms.Push("android");
-				}
-
-				gen->SetSupportedPlatforms(platforms);
+                gen->SetSupportedPlatforms(curBuild_->platforms_);
+				gen->SetRewriteSolution(true);
 
                 if (!gen->LoadJSONProject(solutionPath))
                 {
@@ -229,7 +231,53 @@ namespace ToolCore
                 return;
             }
 
-            const String configuration = curBuild_->configuration_;
+            StringVector stringVector;
+            String platforms;
+            String configs;
+
+            for (unsigned i = 0; i < curBuild_->configurations_.Size(); i++)
+            {
+                stringVector.Push(ToString("/p:Configuration=%s", curBuild_->configurations_[i].CString()));
+            }
+
+            configs = String::Joined(stringVector, " ");
+            stringVector.Clear();
+
+            for (unsigned i = 0; i < curBuild_->platforms_.Size(); i++)
+            {
+                // map platform
+                String platform = curBuild_->platforms_[i];
+
+                if (platform == "desktop" || platform == "android")
+                {
+                    platform = "\"Any CPU\"";
+                }
+                else if (platform == "ios")
+                {
+
+                    platform = "\"Any CPU\"";
+                    // TODO
+                    // platform = "iPhone";
+                }
+                else
+                {
+                    ATOMIC_LOGERRORF("Unknown platform: %s, skipping", platform.CString());
+                    continue;
+                }
+
+                platform = ToString("/p:Platform=%s", platform.CString());
+
+                if (stringVector.Contains(platform))
+                {
+                    // This can happen when specifying Desktop + Android for example
+                    continue;
+                }
+
+                stringVector.Push(platform);
+            }
+
+            platforms = String::Joined(stringVector, " ");
+            stringVector.Clear();
 
             Vector<String> args;
 
@@ -258,7 +306,7 @@ namespace ToolCore
                 compile += ToString("&& \"%s\" restore \"%s\" ", nugetBinary.CString(), solutionPath.CString());
             }
 
-            compile += ToString("&& msbuild \"%s\" /p:Configuration=%s /p:Platform=\"Any CPU\"\"", solutionPath.CString(), configuration.CString());
+            compile += ToString("&& msbuild \"%s\" %s %s\"", solutionPath.CString(), platforms.CString(), configs.CString());
 
             args.Push(compile);
 
@@ -280,7 +328,7 @@ namespace ToolCore
 #endif
             }
 
-            compile += ToString("\"%s\" \"%s\" /p:Configuration=%s /p:Platform=\"Any CPU\"", xbuildBinary.CString(), solutionPath.CString(), configuration.CString());
+            compile += ToString("\"%s\" \"%s\" %s %s", xbuildBinary.CString(), solutionPath.CString(), platforms.CString(), configs.CString());
 
             args.Push(compile);
 
@@ -291,6 +339,8 @@ namespace ToolCore
             SubprocessSystem* subs = GetSubsystem<SubprocessSystem>();
             Subprocess* subprocess = nullptr;
 
+            ATOMIC_LOGINFOF("%s : %s", cmd.CString(), curBuild_->allArgs_.CString());
+
             try
             {
                 subprocess = subs->Launch(cmd, args, "");
@@ -316,7 +366,7 @@ namespace ToolCore
     }
 
 
-    NETBuild* NETBuildSystem::GetBuild(const String& solutionPath, const String& platform, const String& configuration)
+    NETBuild* NETBuildSystem::GetBuild(const String& solutionPath, const StringVector& platforms, const StringVector& configurations)
     {
         List<SharedPtr<NETBuild>>::ConstIterator itr = builds_.Begin();
 
@@ -324,7 +374,7 @@ namespace ToolCore
         {
             NETBuild* build = *itr;
 
-            if (build->solutionPath_ == solutionPath && build->platform_ == platform && build->configuration_ == configuration)
+            if (build->solutionPath_ == solutionPath && build->platforms_ == platforms && build->configurations_ == configurations)
                 return build;
 
             itr++;
@@ -336,26 +386,20 @@ namespace ToolCore
 
     NETBuild* NETBuildSystem::BuildAtomicProject(Project* project)
     {
-        String platform;
-        String configuration;
+        StringVector platforms;
+        StringVector configurations;
 
-#ifdef ATOMIC_PLATFORM_WINDOWS
-        platform = "WINDOWS";
-#elif ATOMIC_PLATFORM_OSX
-        platform = "MACOSX";
-#else
-        platform = "LINUX";
-#endif
+        platforms.Push("desktop");
 
 #ifdef ATOMIC_DEBUG
-        configuration = "Debug";
+        configurations.Push("Debug");
 #else
-        configuration = "Release";
+        configurations.Push("Release");
 #endif
 
 		String solutionPath = project->GetProjectPath() + "AtomicNET/Solution/" + project->GetProjectSettings()->GetName() + ".sln";
 
-        NETBuild* build = Build(solutionPath, platform, configuration);
+        NETBuild* build = Build(solutionPath, platforms, configurations);
 
         if (build)
         {
@@ -385,7 +429,7 @@ namespace ToolCore
 
     }
 
-    NETBuild* NETBuildSystem::Build(const String& solutionPath, const String& platform, const String& configuration)
+    NETBuild* NETBuildSystem::Build(const String& solutionPath, const StringVector& platforms, const StringVector& configurations)
     {
 
         FileSystem* fileSystem = GetSubsystem<FileSystem>();
@@ -397,13 +441,13 @@ namespace ToolCore
         }
 
         // Get existing build
-        SharedPtr<NETBuild> build(GetBuild(solutionPath, platform, configuration));
+        SharedPtr<NETBuild> build(GetBuild(solutionPath, platforms, configurations));
 
         if (build.NotNull())
             return build;
 
         // Create a new build
-        build = new NETBuild(context_, solutionPath, platform, configuration);
+        build = new NETBuild(context_, solutionPath, platforms, configurations);
 
         builds_.Push(build);
 

+ 6 - 5
Source/ToolCore/NETTools/NETBuildSystem.h

@@ -58,14 +58,15 @@ namespace ToolCore
     
     public:
 
-        NETBuild(Context* context, const String& solutionPath, const String& platform, const String& configuration);
+        NETBuild(Context* context, const String& solutionPath, const StringVector& platforms, const StringVector& configurations);
         virtual ~NETBuild() {}
 
     private:
         /// .sln or .json configuration file
         String solutionPath_;
-        String configuration_;
-        String platform_;
+        StringVector configurations_;
+        StringVector platforms_;
+
         NETBuildStatus status_;
         String allArgs_;
         String output_;
@@ -85,7 +86,7 @@ namespace ToolCore
         virtual ~NETBuildSystem();
 
         /// Build either a .sln or .json configuration file
-        NETBuild* Build(const String& solutionPath, const String& platform, const String& configuration = "Release");
+        NETBuild* Build(const String& solutionPath, const StringVector &platforms, const StringVector &configurations);
 
         NETBuild* BuildAtomicProject(Project* project);
 
@@ -93,7 +94,7 @@ namespace ToolCore
 
         void CurrentBuildError(String errorText);
 
-        NETBuild* GetBuild(const String& solutionPath, const String& platform, const String& configuration);
+        NETBuild* GetBuild(const String& solutionPath, const StringVector& platforms, const StringVector& configurations);
 
         void HandleBuildAtomicProject(StringHash eventType, VariantMap& eventData);
         void HandleToolUpdate(StringHash eventType, VariantMap& eventData);

+ 157 - 23
Source/ToolCore/NETTools/NETProjectGen.cpp

@@ -237,6 +237,12 @@ namespace ToolCore
 
 					platform = "Android";
 				}
+                else if (SupportsPlatform("ios"))
+                {
+                    ref = "AtomicNET";
+                    platform = "iOS";
+                }
+
 
 				if (platform.Length())
 				{
@@ -374,10 +380,23 @@ namespace ToolCore
 		paths.Join(searchPaths, ";");
 	}
 
+    void NETCSProject::ProcessDefineConstants(StringVector& constants)
+    {
+        const Vector<String>& globalConstants = projectGen_->GetGlobalDefineConstants();
+        constants += globalConstants;
+
+        if (constants.Contains("ATOMIC_IOS") || constants.Contains("ATOMIC_ANDROID"))
+            constants.Push("ATOMIC_MOBILE");
+    }
+
 	void NETCSProject::CreateReleasePropertyGroup(XMLElement &projectRoot)
 	{
 		XMLElement pgroup = projectRoot.CreateChild("PropertyGroup");
-		pgroup.SetAttribute("Condition", " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ");
+
+        if (playerApplication_ && SupportsPlatform("ios"))
+            pgroup.SetAttribute("Condition", " '$(Configuration)|$(Platform)' == 'Release|iPhone' ");
+        else
+            pgroup.SetAttribute("Condition", " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ");
 
 		pgroup.CreateChild("Optimize").SetValue("true");
 
@@ -391,8 +410,7 @@ namespace ToolCore
 
 		constants += defineConstants_;
 
-		const Vector<String>& globalConstants = projectGen_->GetGlobalDefineConstants();
-		constants += globalConstants;
+        ProcessDefineConstants(constants);
 
 		pgroup.CreateChild("DefineConstants").SetValue(String::Joined(constants, ";").CString());
 		pgroup.CreateChild("ErrorReport").SetValue("prompt");
@@ -429,16 +447,27 @@ namespace ToolCore
 		}
 		else
 		{
-			pgroup.CreateChild("DebugType").SetValue("pdbonly");
-
 			if (SupportsPlatform("android"))
 			{
+                pgroup.CreateChild("DebugType").SetValue("pdbonly");
+
 				if (outputType_.ToLower() != "library")
 				{
 					pgroup.CreateChild("AndroidUseSharedRuntime").SetValue("False");
 					pgroup.CreateChild("AndroidLinkMode").SetValue("SdkOnly");
 				}
 			}
+            else if (playerApplication_ && SupportsPlatform("ios"))
+            {
+                pgroup.CreateChild("DebugType").SetValue("none");
+
+                pgroup.CreateChild("MtouchArch").SetValue("ARMv7, ARM64");
+                pgroup.CreateChild("CodesignEntitlements").SetValue(GetSanitizedPath(codesignEntitlements_));
+                pgroup.CreateChild("CodesignKey").SetValue("iPhone Developer");
+                pgroup.CreateChild("MtouchDebug").SetValue("true");
+                pgroup.CreateChild("MtouchOptimizePNGs").SetValue("False");
+            }
+
 		}
 
 	}
@@ -446,7 +475,12 @@ namespace ToolCore
 	void NETCSProject::CreateDebugPropertyGroup(XMLElement &projectRoot)
 	{
 		XMLElement pgroup = projectRoot.CreateChild("PropertyGroup");
-		pgroup.SetAttribute("Condition", " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ");
+
+        if (playerApplication_ && SupportsPlatform("ios"))
+            pgroup.SetAttribute("Condition", " '$(Configuration)|$(Platform)' == 'Debug|iPhone' ");
+        else
+            pgroup.SetAttribute("Condition", " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ");
+
 
 		pgroup.CreateChild("Optimize").SetValue("false");
 
@@ -460,9 +494,7 @@ namespace ToolCore
 		constants.Push("TRACE");
 
 		constants += defineConstants_;
-
-		const Vector<String>& globalConstants = projectGen_->GetGlobalDefineConstants();
-		constants += globalConstants;
+        ProcessDefineConstants(constants);
 
 		pgroup.CreateChild("DefineConstants").SetValue(String::Joined(constants, ";").CString());
 
@@ -516,6 +548,17 @@ namespace ToolCore
 				pgroup.CreateChild("AndroidEnableMultiDex").SetValue("False");
 				pgroup.CreateChild("AndroidSupportedAbis").SetValue("armeabi-v7a");
 			}
+            else if (playerApplication_ && SupportsPlatform("ios"))
+            {
+                pgroup.CreateChild("MtouchArch").SetValue("ARMv7, ARM64");
+                pgroup.CreateChild("CodesignEntitlements").SetValue(GetSanitizedPath(codesignEntitlements_));
+                pgroup.CreateChild("CodesignKey").SetValue("iPhone Developer");
+                pgroup.CreateChild("MtouchDebug").SetValue("true");
+                pgroup.CreateChild("MtouchFastDev").SetValue("true");
+                pgroup.CreateChild("IpaPackageName").SetValue("");
+                pgroup.CreateChild("OptimizePNGs").SetValue("false");
+                pgroup.CreateChild("MtouchOptimizePNGs").SetValue("False");
+            }
 		}
 
 
@@ -570,17 +613,69 @@ namespace ToolCore
 		}
 		else
 		{
-			const String& projectPath = projectGen_->GetAtomicProjectPath();
+			const String& projectPath = projectGen_->GetAtomicProjectPath();            
 			if (!playerApplication_ || !projectPath.Length())
 				return;
 
-			XMLElement androidAsset = itemGroup.CreateChild("AndroidAsset");
-			androidAsset.SetAttribute("Include", projectPath + "AtomicNET/Resources/AtomicResources.pak");
-			androidAsset.CreateChild("Link").SetValue("Assets\\AtomicResources.pak");
+            if (androidApplication_)
+            {
+                XMLElement androidAsset = itemGroup.CreateChild("AndroidAsset");
+                androidAsset.SetAttribute("Include", projectPath + "AtomicNET/Resources/AtomicResources.pak");
+                androidAsset.CreateChild("Link").SetValue("Assets\\AtomicResources.pak");
+            }
+            else
+            {
+                XMLElement bundleResource = itemGroup.CreateChild("BundleResource");
+                bundleResource.SetAttribute("Include", projectPath + "AtomicNET/Resources/AtomicResources.pak");
+                bundleResource.CreateChild("Link").SetValue("Resources\\AtomicResources.pak");
+                bundleResource.CreateChild("CopyToOutputDirectory").SetValue("PreserveNewest");
+            }
 		}
 
 	}
 
+    void NETCSProject::CreateIOSItems(XMLElement &projectRoot)
+    {
+        XMLElement iosGroup = projectRoot.CreateChild("ItemGroup");
+
+        if (objcBindingApiDefinition_.Length())
+        {
+            iosGroup.CreateChild("ObjcBindingApiDefinition").SetAttribute("Include", GetSanitizedPath(objcBindingApiDefinition_));
+        }
+
+        if (name_ == "AtomicNET.iOS")
+        {
+            ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
+
+#ifdef ATOMIC_DEBUG
+            String config = "Debug";
+#else
+            String config = "Release";
+#endif
+
+            String nativePath = AddTrailingSlash(tenv->GetAtomicNETRootDir()) + config + "/Native/iOS/AtomicNETNative.framework";
+            iosGroup.CreateChild("ObjcBindingNativeFramework").SetAttribute("Include", GetSanitizedPath(nativePath));
+
+            // framework copy
+            XMLElement none = iosGroup.CreateChild("None");
+            none.SetAttribute("Include", nativePath + ".zip");
+            none.CreateChild("Link").SetValue("AtomicNETNative.framework.zip");
+            none.CreateChild("CopyToOutputDirectory").SetValue("Always");
+
+        }
+        else if (playerApplication_)
+        {
+            XMLElement plist = iosGroup.CreateChild("None");
+            plist.SetAttribute("Include", GetSanitizedPath(infoPList_));
+            plist.CreateChild("Link").SetValue("Info.plist");
+
+            XMLElement entitlements = iosGroup.CreateChild("Content");
+            entitlements.SetAttribute("Include", GetSanitizedPath(codesignEntitlements_));
+            entitlements.CreateChild("Link").SetValue("Entitlements.plist");
+        }
+
+    }
+
 	void NETCSProject::CreateAndroidItems(XMLElement &projectRoot)
 	{
 
@@ -729,8 +824,11 @@ namespace ToolCore
 
 		// Platform
 		XMLElement platform = pgroup.CreateChild("Platform");
-		platform.SetAttribute("Condition", " '$(Platform)' == '' ");
-		platform.SetValue("AnyCPU");
+        platform.SetAttribute("Condition", " '$(Platform)' == '' ");
+        if (playerApplication_ && SupportsPlatform("ios"))
+            platform.SetValue("iPhone");
+        else
+            platform.SetValue("AnyCPU");
 
 		// ProjectGuid
 		XMLElement guid = pgroup.CreateChild("ProjectGuid");
@@ -766,7 +864,14 @@ namespace ToolCore
 			pgroup.CreateChild("ProductVersion").SetValue("8.0.30703");
 			pgroup.CreateChild("SchemaVersion").SetValue("2.0");
 
-			pgroup.CreateChild("TargetFrameworkVersion").SetValue("v6.0");
+            if (SupportsPlatform("ios"))
+            {
+                pgroup.CreateChild("IPhoneResourcePrefix").SetValue("Resources");
+            }
+            else
+            {
+                pgroup.CreateChild("TargetFrameworkVersion").SetValue("v6.0");
+            }
 
 			if (SupportsPlatform("android"))
 			{
@@ -945,6 +1050,12 @@ namespace ToolCore
 			CreateAndroidItems(project);
 		}
 
+        if (SupportsPlatform("ios"))
+        {
+            CreateIOSItems(project);
+        }
+
+
 		if (SupportsDesktop() && !GetIsPCL())
 			project.CreateChild("Import").SetAttribute("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets");
 
@@ -1213,6 +1324,16 @@ namespace ToolCore
 
 		targetFrameworkProfile_ = root["targetFrameworkProfile"].GetString();
 
+        // iOS
+        objcBindingApiDefinition_ = root["objcBindingApiDefinition"].GetString();
+        ReplacePathStrings(objcBindingApiDefinition_);
+
+        codesignEntitlements_ = root["codesignEntitlements"].GetString();
+        ReplacePathStrings(codesignEntitlements_);
+
+        infoPList_ = root["infoPList"].GetString();
+        ReplacePathStrings(infoPList_);
+
 		return true;
 	}
 
@@ -1327,7 +1448,9 @@ namespace ToolCore
 
 		source += "    GlobalSection(SolutionConfigurationPlatforms) = preSolution\n";
 		source += "        Debug|Any CPU = Debug|Any CPU\n";
-		source += "        Release|Any CPU = Release|Any CPU\n";
+		source += "        Release|Any CPU = Release|Any CPU\n";        
+        source += "        Debug|iPhone = Debug|iPhone\n";
+        source += "        Release|iPhone = Release|iPhone\n";
 		source += "    EndGlobalSection\n";
 		source += "    GlobalSection(ProjectConfigurationPlatforms) = postSolution\n";
 
@@ -1338,10 +1461,22 @@ namespace ToolCore
 			if (p->outputType_ == "Shared")
 				continue;
 
-			source += ToString("        {%s}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n", p->GetProjectGUID().CString());
-			source += ToString("        {%s}.Debug|Any CPU.Build.0 = Debug|Any CPU\n", p->GetProjectGUID().CString());
-			source += ToString("        {%s}.Release|Any CPU.ActiveCfg = Release|Any CPU\n", p->GetProjectGUID().CString());
-			source += ToString("        {%s}.Release|Any CPU.Build.0 = Release|Any CPU\n", p->GetProjectGUID().CString());
+            String cpu = "Any CPU";
+            if (p->GetIsPlayerApp() && p->SupportsPlatform("ios"))
+                cpu = "iPhone";
+
+            source += ToString("        {%s}.Debug|%s.ActiveCfg = Debug|%s\n", p->GetProjectGUID().CString(), cpu.CString(), cpu.CString());
+            source += ToString("        {%s}.Debug|%s.Build.0 = Debug|%s\n", p->GetProjectGUID().CString(), cpu.CString(), cpu.CString());
+            source += ToString("        {%s}.Release|%s.ActiveCfg = Release|%s\n", p->GetProjectGUID().CString(), cpu.CString(), cpu.CString());
+            source += ToString("        {%s}.Release|%s.Build.0 = Release|%s\n", p->GetProjectGUID().CString(), cpu.CString(), cpu.CString());
+
+            if (cpu != "iPhone" && (p->SupportsPlatform("ios", false)))
+            {                               
+                source += ToString("        {%s}.Debug|iPhone.ActiveCfg = Debug|Any CPU\n", p->GetProjectGUID().CString());
+                source += ToString("        {%s}.Debug|iPhone.Build.0 = Debug|Any CPU\n", p->GetProjectGUID().CString());
+                source += ToString("        {%s}.Release|iPhone.ActiveCfg = Release|Any CPU\n", p->GetProjectGUID().CString());
+                source += ToString("        {%s}.Release|iPhone.Build.0 = Release|Any CPU\n", p->GetProjectGUID().CString());
+            }
 		}
 
 		source += "    EndGlobalSection\n";
@@ -1563,7 +1698,6 @@ namespace ToolCore
 
 	bool NETProjectGen::LoadAtomicProject(const String& atomicProjectPath)
 	{
-		FileSystem* fileSystem = GetSubsystem<FileSystem>();
 		ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
 		ToolSystem* tsystem = GetSubsystem<ToolSystem>();
 
@@ -1589,7 +1723,7 @@ namespace ToolCore
 		{
 			// Nope, load them up
 			projectSettings_ = SharedPtr<ProjectSettings>(new ProjectSettings(context_));
-			projectSettings_->Load(atomicProjectPath_ + "Settings/Platforms.json");
+            projectSettings_->Load(atomicProjectPath_ + "Settings/Project.json");
 		}
 
 #ifdef ATOMIC_DEV_BUILD

+ 13 - 2
Source/ToolCore/NETTools/NETProjectGen.h

@@ -76,6 +76,10 @@ namespace ToolCore
 
 		bool GetIsPCL() const { return projectTypeGuids_.Contains("{786C830F-07A1-408B-BD7F-6EE04809D6DB}"); }
 
+        bool GetIsPlayerApp() const { return playerApplication_; }
+        bool SupportsDesktop() const;
+        bool SupportsPlatform(const String& platform, bool explicitCheck = true) const;
+
         bool Generate();
 
     private:
@@ -99,12 +103,13 @@ namespace ToolCore
 
 		void CreateApplicationItems(XMLElement &projectRoot);
 		void CreateAndroidItems(XMLElement &projectRoot);
+        void CreateIOSItems(XMLElement &projectRoot);
 
         void CreateAssemblyInfo();
         void GetAssemblySearchPaths(String& paths);
 
-		bool SupportsDesktop() const;
-		bool SupportsPlatform(const String& platform, bool explicitCheck = true) const;
+        void ProcessDefineConstants(StringVector& constants);
+
 
         String name_;
         String projectGuid_;
@@ -138,6 +143,12 @@ namespace ToolCore
 
 		// Android
 		bool androidApplication_;
+
+        // iOS
+        String objcBindingApiDefinition_;
+        String codesignEntitlements_;
+        String infoPList_;
+
 		
     };
 

+ 2 - 1
Source/ToolCore/Project/ProjectSettings.cpp

@@ -89,6 +89,7 @@ namespace ToolCore
 
 		if (!fileSystem->FileExists(path))
 		{
+            ATOMIC_LOGERRORF("No platform settings specified, using default: %s", path.CString());
 			SetDefault();
 			return true;
 		}
@@ -144,7 +145,7 @@ namespace ToolCore
 
 		if (!platforms_.Size())
 		{
-			ATOMIC_LOGERRORF("No valif platforms defined in platform settings: %s, using default", path.CString());
+            ATOMIC_LOGERRORF("No valid platforms defined in platform settings: %s, using default", path.CString());
 			SetDefault();
 		}