Browse Source

Merge pull request #977 from AtomicGameEngine/JME-ATOMIC-NETUNIX

AtomicNET support for OSX/Linux including Xamarin Studio integration
JoshEngebretson 9 years ago
parent
commit
7562d3f345

+ 1 - 3
Build/Scripts/BuildCommon.js

@@ -89,9 +89,7 @@ namespace('build', function() {
       // Compile AtomicNET assemblies
       var cmds = [];
 
-      if (os.platform() == "win32") {
-        cmds.push(host.atomicTool + " net compile " + atomicRoot + "Script/AtomicNET/AtomicNETProject.json " + platform + " " + configuration);
-      }
+      cmds.push(host.atomicTool + " net compile " + atomicRoot + "Script/AtomicNET/AtomicNETProject.json " + platform + " " + configuration);
 
       jake.exec(cmds, function() {
 

+ 16 - 0
Build/Scripts/BuildLinux.js

@@ -1,5 +1,6 @@
 var fs = require('fs-extra');
 var path = require("path");
+var spawnSync = require('child_process').spawnSync
 var host = require("./Host");
 var atomicRoot = host.atomicRoot;
 
@@ -21,6 +22,12 @@ namespace('build', function() {
             common.cleanCreateDir(host.getGenScriptRootDir("LINUX"));
         }
 
+        var buildAtomicNET = false;
+
+        // TODO: build box has old node
+        if (spawnSync)
+            buildAtomicNET = spawnSync("which", ["xbuild"]).status == 1 ? false : true;
+
         process.chdir(buildDir);
 
         var cmds = [];
@@ -28,6 +35,9 @@ namespace('build', function() {
         cmds.push("cmake ../../../ -DATOMIC_DEV_BUILD=0 -DCMAKE_BUILD_TYPE=Release");
         cmds.push("make -j2")
 
+        if (buildAtomicNET)
+          cmds.push(host.atomicTool + " net compile " + atomicRoot + "Script/AtomicNET/AtomicNETProject.json LINUX Release");
+
         jake.exec(cmds, function() {
 
             // Copy the Editor binaries
@@ -53,6 +63,12 @@ namespace('build', function() {
             fs.copySync(buildDir +  "Source/AtomicPlayer/Application/AtomicPlayer",
             editorAppFolder + "Resources/ToolData/Deployment/Linux/AtomicPlayer");
 
+            // AtomicNET
+
+            if (buildAtomicNET) {
+              fs.copySync(atomicRoot + "Artifacts/AtomicNET/Release",
+                 editorAppFolder + "Resources/ToolData/AtomicNET/Release");
+            }
 
             var binaryFiles = ["chrome-sandbox", "libcef.so", "natives_blob.bin", "snapshot_blob.bin"];
 

+ 12 - 1
Build/Scripts/BuildMac.js

@@ -1,5 +1,6 @@
 var fs = require('fs-extra');
 var path = require("path");
+var spawnSync = require('child_process').spawnSync
 var host = require("./Host");
 var atomicRoot = host.atomicRoot;
 
@@ -21,12 +22,17 @@ task('atomiceditor', {
     common.cleanCreateDir(host.getGenScriptRootDir("MACOSX"));
   }
 
+  var buildAtomicNET = spawnSync("which", ["xbuild"]).status == 1 ? false : true;
+
   process.chdir(buildDir);
 
   var cmds = [];
 
   cmds.push("cmake ../../../ -DATOMIC_DEV_BUILD=0 -G Xcode");
-  cmds.push("xcodebuild -target AtomicEditor -target AtomicPlayer -configuration Release -parallelizeTargets -jobs 4")
+  cmds.push("xcodebuild -target AtomicEditor -target AtomicPlayer -target AtomicNETNative -configuration Release -parallelizeTargets -jobs 4")
+
+  if (buildAtomicNET)
+    cmds.push(host.atomicTool + " net compile " + atomicRoot + "Script/AtomicNET/AtomicNETProject.json MACOSX Release");
 
   jake.exec(cmds, function() {
 
@@ -56,6 +62,11 @@ task('atomiceditor', {
     fs.copySync(playerBinary,
       resourceDest + "ToolData/Deployment/MacOS/AtomicPlayer.app/Contents/MacOS/AtomicPlayer");
 
+    // AtomicNET
+    if (buildAtomicNET)
+      fs.copySync(atomicRoot + "Artifacts/AtomicNET/Release",
+        resourceDest + "ToolData/AtomicNET/Release");
+
     console.log("\n\nAtomic Editor build to " + editorAppFolder + "\n\n");
 
     complete();

+ 28 - 4
Script/AtomicEditor/hostExtensions/languageExtensions/CSharpLanguageExtension.ts

@@ -60,11 +60,17 @@ export default class CSharpLanguageExtension implements Editor.HostExtensions.Re
 
         if (this.isNETProject && !this.menuCreated) {
 
+            const isCompileOnSave = this.serviceRegistry.projectServices.getUserPreference(this.name, "CompileOnSave", false);
+
             // Build the menu - First build up an empty menu then manually add the items so we can have reference to them
             const menu = this.serviceRegistry.uiServices.createPluginMenuItemSource("AtomicNET", {});
             menu.addItem(new Atomic.UIMenuItem("Open Solution", `${this.name}.opensolution`));
             menu.addItem(new Atomic.UIMenuItem("Compile Project", `${this.name}.compileproject`));
             menu.addItem(new Atomic.UIMenuItem("Generate Solution", `${this.name}.generatesolution`));
+
+            this.compileOnSaveMenuItem = new Atomic.UIMenuItem(`Compile on Save: ${isCompileOnSave ? "On" : "Off"}`, `${this.name}.compileonsave`);
+            menu.addItem(this.compileOnSaveMenuItem);
+
             this.menuCreated = true;
         }
 
@@ -119,12 +125,24 @@ export default class CSharpLanguageExtension implements Editor.HostExtensions.Re
     */
     save(ev: Editor.EditorEvents.SaveResourceEvent) {
 
+        if (Atomic.getExtension(ev.path) != ".cs") {
+            return;
+        }
+
         // let's check to see if we have created a csharp file
         if (!this.isNETProject) {
-            if (Atomic.getExtension(ev.path) == ".cs") {
-                this.isNETProject = true;
-            }
+            this.isNETProject = true;
         }
+
+        const isCompileOnSave = this.serviceRegistry.projectServices.getUserPreference(this.name, "CompileOnSave", false);
+
+        if (isCompileOnSave && ToolCore.netProjectSystem) {
+
+            // for now, only support compile on save when not using VS
+            if (!ToolCore.netProjectSystem.iDEAvailable)
+                ToolCore.netProjectSystem.buildAtomicProject();
+        }
+
     }
 
     /*** ProjectService implementation ****/
@@ -183,7 +201,13 @@ export default class CSharpLanguageExtension implements Editor.HostExtensions.Re
                 case "generatesolution":
                     this.generateSolution();
                     return true;
-
+                case "compileonsave":
+                    let isCompileOnSave = this.serviceRegistry.projectServices.getUserPreference(this.name, "CompileOnSave", false);
+                    // Toggle
+                    isCompileOnSave = !isCompileOnSave;
+                    this.serviceRegistry.projectServices.setUserPreference(this.name, "CompileOnSave", isCompileOnSave);
+                    this.compileOnSaveMenuItem.string = `Compile on Save: ${isCompileOnSave ? "On" : "Off"}`;
+                    return true;
             }
         }
     }

+ 1 - 1
Script/AtomicEditor/ui/resourceEditors/CSharpResourceEditorBuilder.ts

@@ -31,7 +31,7 @@ export default class CSharpResourceEditorBuilder extends AbstractTextResourceEdi
     canHandleResource(resourcePath: string) : boolean {
 
         /// Handled externally by VS, TODO: make this a preference
-        if (ToolCore.netProjectSystem.visualStudioAvailable)
+        if (ToolCore.netProjectSystem.iDEAvailable)
             return false;
 
         var ext = Atomic.getExtension(resourcePath).toLowerCase();

+ 1 - 1
Script/AtomicEditor/ui/resourceEditors/VisualStudioResourceEditorBuilder.ts

@@ -37,7 +37,7 @@ export default class VisualStudioResourceEditorBuilder extends AbstractTextResou
     canHandleResource(resourcePath: string) : boolean {
 
         /// Handled externally by VS, TODO: make this a preference
-        if (!ToolCore.netProjectSystem.visualStudioAvailable)
+        if (!ToolCore.netProjectSystem.iDEAvailable)
             return false;
 
         var ext = Atomic.getExtension(resourcePath).toLowerCase();

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

@@ -89,10 +89,14 @@ namespace AtomicEngine
             NativeCore.Initialize();
             CSComponentCore.Initialize();
 
+            string[] arguments = Environment.GetCommandLineArgs();
+            foreach (string arg in arguments)
+                AppBase.AddArgument(arg);
+
         }
 
         [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
-        private static extern IntPtr csb_Atomic_NETCore_Initialize(ref CoreDelegates delegates);        
+        private static extern IntPtr csb_Atomic_NETCore_Initialize(ref CoreDelegates delegates);
 
         private static Context context;
         private static CoreDelegates coreDelegates;
@@ -100,4 +104,4 @@ namespace AtomicEngine
 
     }
 
-}
+}

+ 5 - 0
Script/AtomicNET/AtomicNET/Core/Constants.cs

@@ -7,7 +7,12 @@ namespace AtomicEngine
 
     public static partial class Constants
     {
+
+#if __MonoCS__
+        public const string LIBNAME = "AtomicNETNative";
+#else
         public const string LIBNAME = "AtomicNETNative.dll";
+#endif
 
     }
 

+ 14 - 1
Source/AtomicApp/AppBase.cpp

@@ -38,12 +38,25 @@ namespace Atomic
 {
 
     Vector<String> AppBase::engineConfigSearchPaths_;
+    Vector<String> AppBase::arguments_;
 
     AppBase::AppBase(Context* context) :
         Application(context)
     {
         // Copy arguments
-        arguments_ = GetArguments();
+        if (!arguments_.Size())
+        {
+            arguments_ = GetArguments();
+        }
+        else
+        {
+            String commandline = String::Joined(arguments_, " ");
+
+            ParseArguments(commandline, false);
+
+            arguments_ = GetArguments();
+        }
+
     }
 
     AppBase::~AppBase()

+ 2 - 2
Source/AtomicApp/AppBase.h

@@ -45,7 +45,7 @@ namespace Atomic
         /// Cleanup after the main loop. 
         virtual void Stop();
 
-        void AddArgument(const String& argument) { arguments_.Push(argument); }
+        static void AddArgument(const String& argument) { arguments_.Push(argument); }
 
         virtual void ProcessArguments();
 
@@ -55,7 +55,7 @@ namespace Atomic
 
         void ReadEngineConfig();
 
-        Vector<String> arguments_;
+        static Vector<String> arguments_;
         static Vector<String> engineConfigSearchPaths_;
 
         SharedPtr<JSVM> vm_;

+ 4 - 1
Source/AtomicApp/Player/IPCPlayerApp.cpp

@@ -119,7 +119,10 @@ namespace Atomic
 #else
 
 #ifdef __APPLE__
-                    engineParameters_["ResourcePrefixPaths"] = "../Resources";
+                    if (!resourcePrefix.Length())
+                    {
+                        engineParameters_["ResourcePrefixPaths"] = "../Resources";
+                    }
 #else
                     if (!resourcePrefix.Length())
                     {

+ 28 - 2
Source/AtomicEditor/EditorMode/AEEditorMode.cpp

@@ -129,6 +129,8 @@ bool EditorMode::PlayProject(String addArgs, bool debug)
 {
     FileSystem* fileSystem = GetSubsystem<FileSystem>();
     ToolSystem* tsystem = GetSubsystem<ToolSystem>();
+    ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
+
     Project* project = tsystem->GetProject();
 
     if (!project)
@@ -166,8 +168,18 @@ bool EditorMode::PlayProject(String addArgs, bool debug)
     vargs = args.Split(' ');
 
     if (managed)
-    {            
-        vargs.Insert(0, ToString("\"%s\"", (fileSystem->GetProgramDir() + "Resources/").CString()));        
+    {
+#ifdef ATOMIC_DEV_BUILD
+        vargs.Insert(0, ToString("\"%s/Resources/\"", tenv->GetRootSourceDir().CString()));
+#else
+
+#ifdef ATOMIC_PLATFORM_OSX
+        vargs.Insert(0, ToString("\"%s\"", (fileSystem->GetProgramDir() + "../Resources/").CString()));
+#else
+        vargs.Insert(0, ToString("\"%s\"", (fileSystem->GetProgramDir() + "Resources/").CString()));
+#endif
+
+#endif
         vargs.Insert(0, "--resourcePrefix");
     }
 
@@ -177,6 +189,20 @@ bool EditorMode::PlayProject(String addArgs, bool debug)
     if (addArgs.Length() > 0)
         vargs.Insert(0, addArgs.Split(' '));
 
+#ifndef ATOMIC_PLATFORM_WINDOWS
+    if (managed)
+    {
+        vargs.Insert(0, playerBinary);
+
+#ifdef ATOMIC_PLATFORM_OSX
+        playerBinary = tenv->GetMonoExecutableDir() + "mono64";
+#else
+        playerBinary = "mono";
+#endif
+    }
+
+#endif
+
     String dump;
     dump.Join(vargs, " ");
     ATOMIC_LOGINFOF("Launching Broker %s %s", playerBinary.CString(), dump.CString());

+ 8 - 1
Source/AtomicNET/NETNative/CMakeLists.txt

@@ -19,7 +19,14 @@ add_library(AtomicNETNative SHARED ${SOURCE_FILES}  ${CSHARP_BINDINGS_SOURCE})
 
 add_dependencies(AtomicNETNative AtomicToolCheckScripts)
 
-target_link_libraries(AtomicNETNative AtomicNETScriptBindings AtomicNETScript AtomicJS AtomicPlayerLib AtomicPlayerJS AtomicApp ${ATOMIC_LINK_LIBRARIES})
+target_link_libraries(AtomicNETNative AtomicApp AtomicNETScriptBindings AtomicNETScript AtomicJS AtomicPlayerLib AtomicPlayerJS ${ATOMIC_LINK_LIBRARIES})
+
+if (LINUX)
+
+#https://github.com/nothings/stb/issues/280
+target_link_libraries(AtomicNETNative gcc_s gcc)
+
+endif()
 
 if (APPLE)
 

+ 0 - 7
Source/AtomicNET/NETNative/NETAtomicPlayer.cpp

@@ -45,13 +45,6 @@ namespace Atomic
         PlayerApp(context)
     {
 
-#ifdef WIN32
-        ParseArguments(GetCommandLineW());
-#else
-        #warning Implement Argument Parsing
-#endif
-        arguments_ = GetArguments();
-
     }
 
     void NETAtomicPlayer::Setup()

+ 0 - 7
Source/AtomicNET/NETNative/NETIPCPlayerApp.cpp

@@ -44,13 +44,6 @@ namespace Atomic
         IPCPlayerApp(context)
     {
 
-#ifdef WIN32
-        ParseArguments(GetCommandLineW());
-#else
-        #warning Implement Argument Parsing
-#endif
-        arguments_ = GetArguments();
-
     }
 
     void NETIPCPlayerApp::Setup()

+ 0 - 10
Source/AtomicNET/NETNative/NETServiceApplication.cpp

@@ -42,13 +42,6 @@ namespace Atomic
         IPCClientApp(context)
     {
 
-#ifdef WIN32
-        ParseArguments(GetCommandLineW());        
-#else
-        #warning Implement Argument Parsing
-#endif
-        arguments_ = GetArguments();
-
     }
 
     void NETServiceApplication::Setup()
@@ -58,9 +51,6 @@ namespace Atomic
         // NETService is always headless
         engineParameters_["Headless"] = true;
 
-        // FIXME AtomicNET:
-        // engineParameters_["ResourcePrefixPaths"] = "C:/Dev/atomic/AtomicGameEngine/Resources/";
-
         FileSystem* filesystem = GetSubsystem<FileSystem>();
         engineParameters_.InsertNew("LogName", filesystem->GetAppPreferencesDir("AtomicEditor", "Logs") + "NETServiceApplication.log");
 

+ 0 - 15
Source/ToolCore/Command/NETCmd.cpp

@@ -170,21 +170,6 @@ void NETCmd::Run()
         netService_ = new AtomicNETService(context_);
         context_->RegisterSubsystem(netService_);
 
-        /*
-
-        VariantMap cmd;
-        cmd["command"] = "parse";
-        cmd["assemblyPath"] = assemblyPath_;
-
-        netService_->QueueCommand(cmd);
-
-        cmd.Clear();
-        cmd["command"] = "exit";
-
-        netService_->QueueCommand(cmd);
-
-        */
-
         if (!netService_->Start())
         {
             Error("Unable to start AtomicNETService");

+ 9 - 16
Source/ToolCore/NETTools/AtomicNETService.cpp

@@ -54,31 +54,24 @@ namespace ToolCore
     {
         // TODO: Needs to handle deployed tooling, release builds
 
-        execPath = String::EMPTY;
-        args.Clear();
-
-#ifdef ATOMIC_PLATFORM_WINDOWS
-
         ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
 
-#ifdef _DEBUG
+        execPath = String::EMPTY;
+        args.Clear();
 
-        String config = "Debug";
-#else
-        String config = "Release";
-#endif
+#ifdef ATOMIC_PLATFORM_WINDOWS        
 
         execPath = tenv->GetAtomicNETCoreAssemblyDir() + "/AtomicNETService.exe";
 
-#elif ATOMIC_PLATFORM_OSX
+#elif defined ATOMIC_PLATFORM_OSX
 
-        execPath = "/usr/local/share/dotnet/dotnet";
+        execPath = tenv->GetMonoExecutableDir() + "mono64";
+        args.Push(tenv->GetAtomicNETCoreAssemblyDir() + "AtomicNETService.exe");
 
-        args.Push("exec");
-        args.Push("--additionalprobingpath");
-        args.Push("/Users/josh/.nuget/packages");
-        args.Push("/Users/josh/Dev/atomic/AtomicGameEngine/Script/AtomicNET/AtomicNETService/bin/Debug/netcoreapp1.0/AtomicNETService.exe");
+#elif defined ATOMIC_PLATFORM_LINUX
 
+        execPath = "/usr/bin/mono";
+        args.Push(tenv->GetAtomicNETCoreAssemblyDir() + "AtomicNETService.exe");
 #endif
 
         FileSystem* fileSystem = GetSubsystem<FileSystem>();

+ 33 - 5
Source/ToolCore/NETTools/NETBuildSystem.cpp

@@ -250,6 +250,12 @@ namespace ToolCore
                 return;
             }
 
+            const String configuration = curBuild_->configuration_;
+
+            Vector<String> args;
+
+#ifdef ATOMIC_PLATFORM_WINDOWS
+
             String cmdToolsPath = Poco::Environment::get("VS140COMNTOOLS").c_str();
 
             if (!cmdToolsPath.Length())
@@ -260,10 +266,8 @@ namespace ToolCore
 
             String vcvars64 = ToString("%s..\\..\\VC\\bin\\amd64\\vcvars64.bat", cmdToolsPath.CString());
 
-            const String configuration = curBuild_->configuration_;
-
             String cmd = "cmd";
-            Vector<String> args;
+
             args.Push("/A");
             args.Push("/C");
 
@@ -279,6 +283,30 @@ namespace ToolCore
 
             args.Push(compile);
 
+#else
+
+            String compile;
+
+            String cmd = "bash";
+            args.Push("-c");
+
+            String xbuildBinary = tenv->GetMonoExecutableDir() + "xbuild";
+
+            if (requiresNuGet)
+            {
+#ifdef ATOMIC_PLATFORM_OSX
+                compile += ToString("\"%s\" restore \"%s\" && ", nugetBinary.CString(), solutionPath.CString());
+#else
+                compile += ToString("mono \"%s\" restore \"%s\" && ", nugetBinary.CString(), solutionPath.CString());
+#endif
+            }
+
+            compile += ToString("\"%s\" \"%s\" /p:Configuration=%s /p:Platform=\"Any CPU\"", xbuildBinary.CString(), solutionPath.CString(), configuration.CString());
+
+            args.Push(compile);
+
+#endif
+
             curBuild_->allArgs_.Join(args, " ");
 
             SubprocessSystem* subs = GetSubsystem<SubprocessSystem>();
@@ -286,7 +314,7 @@ namespace ToolCore
 
             try
             {
-                subprocess = subs->Launch(cmd, args, "C:\\");
+                subprocess = subs->Launch(cmd, args, "");
             }
             catch (Poco::SystemException)
             {
@@ -340,7 +368,7 @@ namespace ToolCore
         platform = "LINUX";
 #endif
 
-#ifdef _DEBUG
+#ifdef ATOMIC_DEBUG
         configuration = "Debug";
 #else
         configuration = "Release";

+ 1 - 1
Source/ToolCore/NETTools/NETBuildSystem.h

@@ -59,7 +59,7 @@ namespace ToolCore
     public:
 
         NETBuild(Context* context, const String& solutionPath, const String& platform, const String& configuration);
-        virtual ~NETBuild() {};
+        virtual ~NETBuild() {}
 
     private:
         /// .sln or .json configuration file

+ 59 - 2
Source/ToolCore/NETTools/NETProjectGen.cpp

@@ -298,6 +298,45 @@ namespace ToolCore
         paths.Join(searchPaths, ";");
     }
 
+    void NETCSProject::CreateCustomCommands(XMLElement &propertyGroup, const String& cfg)
+    {
+        Project* atomicProject = projectGen_->GetAtomicProject();
+
+        if (!atomicProject)
+            return;
+
+        ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
+
+        XMLElement customCommands = propertyGroup.CreateChild("CustomCommands").CreateChild("CustomCommands");
+
+        XMLElement xcommand = customCommands.CreateChild("Command");
+
+        xcommand.SetAttribute("type", "Execute");
+
+        String startArguments;
+
+#ifdef ATOMIC_DEV_BUILD
+            String playerBin = tenv->GetAtomicNETRootDir() + cfg + "/AtomicIPCPlayer.exe";
+#else
+            FileSystem* fileSystem = GetSubsystem<FileSystem>();
+            String playerBin = tenv->GetAtomicNETRootDir() + "Release/AtomicIPCPlayer.exe";            
+
+#ifdef ATOMIC_PLATFORM_OSX
+            startArguments += ToString("--resourcePrefix \"%s\" ", (fileSystem->GetProgramDir() + "../Resources/").CString());
+#else
+            startArguments += ToString("--resourcePrefix \"%s\" ", (fileSystem->GetProgramDir() + "Resources/").CString());
+#endif
+        #endif
+
+
+        startArguments += ToString("--project \"%s\"", atomicProject->GetProjectPath().CString());
+
+        String command = ToString("\"%s\"", playerBin.CString()) + " " + startArguments;
+
+        xcommand.SetAttribute("command", command);
+
+    }
+
     void NETCSProject::CreateReleasePropertyGroup(XMLElement &projectRoot)
     {
         XMLElement pgroup = projectRoot.CreateChild("PropertyGroup");
@@ -314,6 +353,10 @@ namespace ToolCore
         pgroup.CreateChild("AllowUnsafeBlocks").SetValue("true");
         pgroup.CreateChild("PlatformTarget").SetValue("x64");
 
+#ifndef ATOMIC_PLATFORM_WINDOWS
+        CreateCustomCommands(pgroup, "Release");
+#endif
+
     }
 
     void NETCSProject::CreateDebugPropertyGroup(XMLElement &projectRoot)
@@ -332,6 +375,10 @@ namespace ToolCore
         pgroup.CreateChild("AllowUnsafeBlocks").SetValue("true");
         pgroup.CreateChild("PlatformTarget").SetValue("x64");
 
+#ifndef ATOMIC_PLATFORM_WINDOWS
+        CreateCustomCommands(pgroup, "Debug");
+#endif
+
     }
 
     void NETCSProject::CreateAssemblyInfo()
@@ -444,6 +491,7 @@ namespace ToolCore
         {
             XMLElement afterBuild = project.CreateChild("Target");
             afterBuild.SetAttribute("Name", "AfterBuild");
+
             XMLElement copy = afterBuild.CreateChild("Copy");
             copy.SetAttribute("SourceFiles", "$(TargetPath)");
 
@@ -457,6 +505,14 @@ namespace ToolCore
 
             copy.SetAttribute("DestinationFolder", destPath);
 
+#ifndef ATOMIC_PLATFORM_WINDOWS
+
+            copy = afterBuild.CreateChild("Copy");
+            copy.SetAttribute("SourceFiles", "$(TargetPath).mdb");
+            copy.SetAttribute("DestinationFolder", destPath);
+
+#endif
+
             // Create the AtomicProject.csproj.user file if it doesn't exist
             String userSettingsFilename = projectPath_ + name_ + ".csproj.user";
             if (!fileSystem->FileExists(userSettingsFilename))
@@ -615,8 +671,9 @@ namespace ToolCore
             const String& projectName = p->GetName();
             const String& projectGUID = p->GetProjectGUID();
 
+            const String CSharpProjectGUID = "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC";
             source += ToString("Project(\"{%s}\") = \"%s\", \"%s\\%s.csproj\", \"{%s}\"\n",
-                solutionGUID_.CString(), projectName.CString(), projectName.CString(),
+                CSharpProjectGUID.CString(), projectName.CString(), projectName.CString(),
                 projectName.CString(), projectGUID.CString());
 
             projectGen_->GetCSProjectDependencies(p, depends);
@@ -634,7 +691,7 @@ namespace ToolCore
                 source += "\tEndProjectSection\n";
             }
 
-            source += "\tEndProject\n";
+            source += "EndProject\n";
         }
 
         source += "Global\n";

+ 3 - 0
Source/ToolCore/NETTools/NETProjectGen.h

@@ -84,6 +84,9 @@ namespace ToolCore
         void CreateMainPropertyGroup(XMLElement &projectRoot);
         void CreateDebugPropertyGroup(XMLElement &projectRoot);
         void CreateReleasePropertyGroup(XMLElement &projectRoot);
+
+        void CreateCustomCommands(XMLElement &propertyGroup, const String& cfg);
+
         void CreateAssemblyInfo();
         void GetAssemblySearchPaths(String& paths);
 

+ 59 - 20
Source/ToolCore/NETTools/NETProjectSystem.cpp

@@ -65,7 +65,7 @@ namespace ToolCore
 
     void NETProjectSystem::OpenSolution()
     {
-        if (!visualStudioPath_.Length())
+        if (!idePath_.Length())
             return;
 
         FileSystem* fileSystem = GetSubsystem<FileSystem>();
@@ -82,28 +82,37 @@ namespace ToolCore
 
     void NETProjectSystem::OpenSourceFile(const String& sourceFilePath)
     {
-        if (!visualStudioPath_.Length())
+        if (!idePath_.Length())
             return;
 
+        String command = idePath_;
         StringVector args;
 
-        if (vsSubprocess_.Expired())
+        if (ideSubprocess_.Expired())
         {
             SubprocessSystem* subs = GetSubsystem<SubprocessSystem>();
-            vsSubprocess_ = 0;
+            ideSubprocess_ = 0;
+
+#ifdef ATOMIC_PLATFORM_OSX
+
+            command = "open";
+            args.Push("-W");
+            args.Push("-a");
+            args.Push(idePath_);
+#endif
 
             args.Push(solutionPath_);
 
             if (sourceFilePath.Length())
-                args.Push(sourceFilePath);
+                args.Push(sourceFilePath);                       
 
             try
             {
-                vsSubprocess_ = subs->Launch(visualStudioPath_, args);
+                ideSubprocess_ = subs->Launch(command, args);
             }
             catch (Poco::SystemException)
             {
-                vsSubprocess_ = 0;
+                ideSubprocess_ = 0;
             }
 
         }
@@ -113,11 +122,26 @@ namespace ToolCore
                 return;
 
             try
-            {
+            {                
                 std::vector<std::string> args;
+
+#ifdef ATOMIC_PLATFORM_WINDOWS
+
                 args.push_back("/edit");
+
+#elif defined ATOMIC_PLATFORM_OSX
+
+                command = "open";
+                args.push_back("-a");
+                args.push_back(idePath_.CString());
+
+#elif defined ATOMIC_PLATFORM_LINUX
+
+                args.push_back(idePath_.CString());
+
+#endif
                 args.push_back(sourceFilePath.CString());
-                Poco::Process::launch(visualStudioPath_.CString(), args);
+                Poco::Process::launch(command.CString(), args);
 
             }
             catch (Poco::SystemException)
@@ -159,8 +183,6 @@ namespace ToolCore
             }
         }
 
-#ifdef ATOMIC_PLATFORM_WINDOWS
-
         Project* project = GetSubsystem<ToolSystem>()->GetProject();
         NETBuildSystem* buildSystem = GetSubsystem<NETBuildSystem>();
 
@@ -174,8 +196,6 @@ namespace ToolCore
             }
 
         }
-#endif
-
     }
 
     bool NETProjectSystem::GenerateSolution()
@@ -336,8 +356,6 @@ namespace ToolCore
     {
         Clear();
 
-#ifdef ATOMIC_PLATFORM_WINDOWS
-
         SubscribeToEvent(E_UPDATE, ATOMIC_HANDLER(NETProjectSystem, HandleUpdate));
 
         SubscribeToEvent(E_PROJECTLOADED, ATOMIC_HANDLER(NETProjectSystem, HandleProjectLoaded));
@@ -355,17 +373,38 @@ namespace ToolCore
         SubscribeToEvent(E_ASSETRENAMED, ATOMIC_HANDLER(NETProjectSystem, HandleAssetRenamed));
         SubscribeToEvent(E_ASSETMOVED, ATOMIC_HANDLER(NETProjectSystem, HandleAssetMoved));
 
+
+#ifdef ATOMIC_PLATFORM_WINDOWS
+
         FileSystem* fileSystem = GetSubsystem<FileSystem>();
 
         // Query for Visual Studio 2015 path
-        visualStudioPath_ = Poco::Environment::get("VS140COMNTOOLS").c_str();
+        idePath_ = Poco::Environment::get("VS140COMNTOOLS").c_str();
 
-        if (visualStudioPath_.Length())
+        if (idePath_.Length())
         {
-            visualStudioPath_.Replace("Tools\\", "IDE\\devenv.exe");
+            idePath_.Replace("Tools\\", "IDE\\devenv.exe");
 
-            if (!fileSystem->FileExists(visualStudioPath_))
-                visualStudioPath_.Clear();
+            if (!fileSystem->FileExists(idePath_))
+                idePath_.Clear();
+        }
+
+#elif defined ATOMIC_PLATFORM_OSX
+
+        FileSystem* fileSystem = GetSubsystem<FileSystem>();
+
+        if (fileSystem->DirExists("/Applications/Xamarin Studio.app"))
+        {
+            idePath_ = "/Applications/Xamarin Studio.app/Contents/MacOS/XamarinStudio";
+        }
+
+#elif defined ATOMIC_PLATFORM_LINUX
+
+        FileSystem* fileSystem = GetSubsystem<FileSystem>();
+
+        if (fileSystem->FileExists("/usr/bin/monodevelop"))
+        {
+            idePath_ = "/usr/bin/monodevelop";
         }
 
 #endif

+ 3 - 3
Source/ToolCore/NETTools/NETProjectSystem.h

@@ -50,7 +50,7 @@ namespace ToolCore
         NETProjectSystem(Context* context);
         virtual ~NETProjectSystem();
 
-        bool GetVisualStudioAvailable() const { return visualStudioPath_.Length() != 0; }
+        bool GetIDEAvailable() const { return idePath_.Length() != 0; }
 
         const String& GetSolutionPath() const { return solutionPath_; }
 
@@ -86,7 +86,7 @@ namespace ToolCore
         void Clear();
         void Initialize();
 
-        String visualStudioPath_;
+        String idePath_;
 
         String solutionPath_;
         String projectAssemblyPath_;
@@ -97,7 +97,7 @@ namespace ToolCore
         bool  projectAssemblyDirty_;
 
         // Visual Studio subprocess
-        WeakPtr<Subprocess> vsSubprocess_;
+        WeakPtr<Subprocess> ideSubprocess_;
 
     };
 

+ 17 - 3
Source/ToolCore/ToolEnvironment.cpp

@@ -76,13 +76,18 @@ bool ToolEnvironment::InitFromPackage()
 
     // AtomicNET
 
-    // atomicNETNuGetBinary_ = ToString("%sBuild/Managed/nuget/nuget.exe", rootSourceDir_.CString());
+    // atomicNETNuGetBinary_ = ToString("%sBuild/Managed/nuget/nuget.exe", rootSourceDir_.CString());       
 
     atomicNETRootDir_ = resourcesDir + "ToolData/AtomicNET/";
     atomicNETCoreAssemblyDir_ = atomicNETRootDir_ + "Release/";
     atomicNETManagedPlayerBinary_ = atomicNETCoreAssemblyDir_ + "AtomicPlayer.exe";
     atomicNETManagedIPCPlayerBinary_ = atomicNETCoreAssemblyDir_ + "AtomicIPCPlayer.exe";
 
+#ifdef ATOMIC_PLATFORM_OSX
+    monoExecutableDir_ = "/Library/Frameworks/Mono.framework/Versions/Current/Commands/";
+    atomicNETNuGetBinary_ = monoExecutableDir_ + "nuget";
+#endif
+
     return true;
 }
 
@@ -169,19 +174,28 @@ void ToolEnvironment::SetRootSourceDir(const String& sourceDir)
 
     // AtomicNET
 
-#ifdef _DEBUG
+#ifdef ATOMIC_DEBUG
     String config = "Debug";
 #else
     String config = "Release";
 #endif
 
-    atomicNETNuGetBinary_ = ToString("%sBuild/Managed/nuget/nuget.exe", rootSourceDir_.CString());
+
 
     atomicNETRootDir_ = rootSourceDir_ + "Artifacts/AtomicNET/";
     atomicNETCoreAssemblyDir_ = rootSourceDir_ + "Artifacts/AtomicNET/" + config + "/";
     atomicNETManagedPlayerBinary_ = atomicNETCoreAssemblyDir_ + "AtomicPlayer.exe";
     atomicNETManagedIPCPlayerBinary_ = atomicNETCoreAssemblyDir_ + "AtomicIPCPlayer.exe";
 
+#if defined ATOMIC_PLATFORM_WINDOWS || defined ATOMIC_PLATFORM_LINUX
+    atomicNETNuGetBinary_ = ToString("%sBuild/Managed/nuget/nuget.exe", rootSourceDir_.CString());
+#endif
+
+#ifdef ATOMIC_PLATFORM_OSX
+    monoExecutableDir_ = "/Library/Frameworks/Mono.framework/Versions/Current/Commands/";
+    atomicNETNuGetBinary_ = monoExecutableDir_ + "nuget";
+#endif
+
 }
 
 void ToolEnvironment::SetRootBuildDir(const String& buildDir, bool setBinaryPaths)

+ 4 - 0
Source/ToolCore/ToolEnvironment.h

@@ -87,6 +87,9 @@ public:
     const String& GetAtomicNETManagedPlayerBinary() { return atomicNETManagedPlayerBinary_; }
     const String& GetAtomicNETManagedIPCPlayerBinary() { return atomicNETManagedIPCPlayerBinary_; }
 
+    const String& GetMonoExecutableDir() { return monoExecutableDir_; }
+
+
 
     // OSX
     const String& GetPlayerAppFolder() { return playerAppFolder_; }
@@ -147,6 +150,7 @@ private:
     String atomicNETNuGetBinary_;
     String atomicNETManagedPlayerBinary_;
     String atomicNETManagedIPCPlayerBinary_;
+    String monoExecutableDir_;
 
     SharedPtr<ToolPrefs> toolPrefs_;
 };