[VS Addin] Add automatic nmf creation

BUG=

Review URL: https://codereview.chromium.org/11348050

git-svn-id: https://nativeclient-sdk.googlecode.com/svn/trunk/src@1462 050acbb0-2703-11df-ab0a-9f3f633ae91d
diff --git a/InstallerResources/NaCl/NaCl.Toolset.default.props b/InstallerResources/NaCl/NaCl.Toolset.default.props
index 0cbc401..781803e 100644
--- a/InstallerResources/NaCl/NaCl.Toolset.default.props
+++ b/InstallerResources/NaCl/NaCl.Toolset.default.props
@@ -41,6 +41,7 @@
       <TranslateX86               Condition="'%(Link.TranslateX86)'                   == ''">true</TranslateX86>
       <TranslateX64               Condition="'%(Link.TranslateX64)'                   == ''">true</TranslateX64>
       <TranslateARM               Condition="'%(Link.TranslateARM)'                   == ''">true</TranslateARM>
+      <CreateNMF                  Condition="'%(Link.CreateNMF)'                      == ''">true</CreateNMF>
   </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup>
diff --git a/InstallerResources/NaCl/Props/nacl_link.xml b/InstallerResources/NaCl/Props/nacl_link.xml
index 881f646..ded04f5 100644
--- a/InstallerResources/NaCl/Props/nacl_link.xml
+++ b/InstallerResources/NaCl/Props/nacl_link.xml
@@ -36,6 +36,8 @@
   <BoolProperty Name="ReportUndefinedSymbols" DisplayName="Report Undefined Symbols" Category="Advanced" Switch="Wl,--no-undefined">
   </BoolProperty>
 
+  <BoolProperty Name="CreateNMF" DisplayName="Create NMF Automatically" Category="General">
+  </BoolProperty>
 
   <StringProperty Name="AdditionalOptions" DisplayName="Additional Options" Description="Additional Options" Category="Command Line">
   </StringProperty>
diff --git a/InstallerResources/NaCl64/Microsoft.Cpp.NaCl64.targets b/InstallerResources/NaCl64/Microsoft.Cpp.NaCl64.targets
index eab467c..ba5c23a 100644
--- a/InstallerResources/NaCl64/Microsoft.Cpp.NaCl64.targets
+++ b/InstallerResources/NaCl64/Microsoft.Cpp.NaCl64.targets
@@ -54,16 +54,20 @@
     <NaClLink BuildingInIDE                  ="$(BuildingInsideVisualStudio)"
               Sources                        ="@(Link)"
               OutputCommandLine              ="%(Link.OutputCommandLine)"
-              NaClLinkerPath                 ="$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\bin\$(TargetArchitecture)-nacl-g++.exe"
+              NaClLinkerPath                 ="$(VSNaClSDKRoot)toolchain\win_x86_$(ToolchainName)\bin\$(TargetArchitecture)-nacl-g++.exe"
+              CreateNMFPath                  ="$(VSNaClSDKRoot)tools\create_nmf.py"
               MinimalRebuildFromTracking     ="$(Link_MinimalRebuildFromTracking)"
               OutputFile                     ="%(Link.OutputFile)"
               PropertiesFile                 ="$(VCTargetsPath)\NaCl\Props\nacl_link.xml"
               TLogReadFiles                  ="@(LinkTLogReadFiles)"
               TLogWriteFiles                 ="@(LinkTLogWriteFiles)"
               Platform                       ="$(Platform)"
+              ToolchainName                  ="$(ToolchainName)"
               TrackFileAccess                ="$(TrackFileAccess)"
               TrackerLogDirectory            ="%(Link.TrackerLogDirectory)"
-        ConfigurationType              ="$(ConfigurationType)">
+              CreateNMF                      ="%(Link.CreateNMF)"
+              ProjectName                    ="$(ProjectName)"
+              ConfigurationType              ="$(ConfigurationType)">
       <Output TaskParameter="SkippedExecution" PropertyName="LinkSkippedExecution" />
     </NaClLink>
 
diff --git a/InstallerResources/PNaCl/Microsoft.Cpp.PNaCl.targets b/InstallerResources/PNaCl/Microsoft.Cpp.PNaCl.targets
index 2bdb650..9450de7 100644
--- a/InstallerResources/PNaCl/Microsoft.Cpp.PNaCl.targets
+++ b/InstallerResources/PNaCl/Microsoft.Cpp.PNaCl.targets
@@ -60,6 +60,7 @@
               PropertiesFile                 ="$(VCTargetsPath)\NaCl\Props\pnacl_link.xml"
               TLogReadFiles                  ="@(LinkTLogReadFiles)"
               TLogWriteFiles                 ="@(LinkTLogWriteFiles)"
+              ToolchainName                  ="$(ToolchainName)"
               Platform                       ="$(Platform)"
               TranslateX86                   ="%(Link.TranslateX86)"
               TranslateX64                   ="%(Link.TranslateX64)"
diff --git a/InstallerResources/examples/hello_nacl/hello_nacl/hello_nacl.vcxproj b/InstallerResources/examples/hello_nacl/hello_nacl/hello_nacl.vcxproj
index 75cc376..b9a6909 100644
--- a/InstallerResources/examples/hello_nacl/hello_nacl/hello_nacl.vcxproj
+++ b/InstallerResources/examples/hello_nacl/hello_nacl/hello_nacl.vcxproj
@@ -147,45 +147,21 @@
     <Link>
       <AdditionalDependencies>ppapi_cpp;ppapi;</AdditionalDependencies>
     </Link>
-    <PostBuildEvent>
-      <Command>set PATH=$(Path)
-mkdir $(ToolchainName)
-python.exe "$(VSNaClSDKRoot)\tools\create_nmf.py" -D "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\bin\x86_64-nacl-objdump.exe" -L "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\x86_64-nacl\lib" -L "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\x86_64-nacl\lib32" -o $(ToolchainName)\$(ProjectName).nmf -t $(ToolchainName) -s $(ToolchainName) "$(TargetPath)"</Command>
-      <Message>Running 'create_nmf'</Message>
-    </PostBuildEvent>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|NaCl32'">
     <Link>
       <AdditionalDependencies>ppapi_cpp;ppapi;</AdditionalDependencies>
     </Link>
-    <PostBuildEvent>
-      <Command>set PATH=$(Path)
-mkdir $(ToolchainName)
-python.exe "$(VSNaClSDKRoot)\tools\create_nmf.py" -D "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\bin\x86_64-nacl-objdump.exe" -L "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\x86_64-nacl\lib" -L "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\x86_64-nacl\lib32" -o $(ToolchainName)\$(ProjectName).nmf -t $(ToolchainName) -s $(ToolchainName) "$(TargetPath)"</Command>
-      <Message>Running 'create_nmf'</Message>
-    </PostBuildEvent>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|NaCl64'">
     <Link>
       <AdditionalDependencies>ppapi_cpp;ppapi;</AdditionalDependencies>
     </Link>
-    <PostBuildEvent>
-      <Command>set PATH=$(Path)
-mkdir $(ToolchainName)
-python.exe "$(VSNaClSDKRoot)\tools\create_nmf.py" -D "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\bin\x86_64-nacl-objdump.exe" -L "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\x86_64-nacl\lib" -L "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\x86_64-nacl\lib32" -o $(ToolchainName)\$(ProjectName).nmf -t $(ToolchainName) -s $(ToolchainName) "$(TargetPath)"</Command>
-      <Message>Running 'create_nmf'</Message>
-    </PostBuildEvent>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|NaCl32'">
     <Link>
       <AdditionalDependencies>ppapi_cpp;ppapi;</AdditionalDependencies>
     </Link>
-    <PostBuildEvent>
-      <Command>set PATH=$(Path)
-mkdir $(ToolchainName)
-python.exe "$(VSNaClSDKRoot)\tools\create_nmf.py" -D "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\bin\x86_64-nacl-objdump.exe" -L "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\x86_64-nacl\lib" -L "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\x86_64-nacl\lib32" -o $(ToolchainName)\$(ProjectName).nmf -t $(ToolchainName) -s $(ToolchainName) "$(TargetPath)"</Command>
-      <Message>Running 'create_nmf'</Message>
-    </PostBuildEvent>
   </ItemDefinitionGroup>
   <ItemGroup>
     <None Include="index.html" />
diff --git a/InstallerResources/examples/hello_world_gles/hello_world_gles/PNaCl/hello_world_gles.nmf b/InstallerResources/examples/hello_world_gles/hello_world_gles/PNaCl/hello_world_gles.nmf
deleted file mode 100644
index ccb21f8..0000000
--- a/InstallerResources/examples/hello_world_gles/hello_world_gles/PNaCl/hello_world_gles.nmf
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "files": {},
-  "program": {
-    "x86-64": {
-      "url": "hello_world_gles_x86_64.nexe"
-    }
-  }
-}
diff --git a/InstallerResources/examples/hello_world_gles/hello_world_gles/hello_world_gles.vcxproj b/InstallerResources/examples/hello_world_gles/hello_world_gles/hello_world_gles.vcxproj
index 05fa79d..c10eaca 100644
--- a/InstallerResources/examples/hello_world_gles/hello_world_gles/hello_world_gles.vcxproj
+++ b/InstallerResources/examples/hello_world_gles/hello_world_gles/hello_world_gles.vcxproj
@@ -69,23 +69,11 @@
     <Link>
       <AdditionalDependencies>ppapi;ppapi_gles2</AdditionalDependencies>
     </Link>
-    <PostBuildEvent>
-      <Command>set PATH=$(Path)
-mkdir $(ToolchainName)
-python.exe "$(VSNaClSDKRoot)\tools\create_nmf.py" -D "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\bin\x86_64-nacl-objdump.exe" -L "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\x86_64-nacl\lib" -L "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\x86_64-nacl\lib32" -o $(ToolchainName)\hello_world_gles.nmf -t $(ToolchainName) -s $(ToolchainName) "$(TargetPath)"</Command>
-      <Message>Running 'create_nmf'</Message>
-    </PostBuildEvent>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|NaCl32'">
     <Link>
       <AdditionalDependencies>ppapi;ppapi_gles2</AdditionalDependencies>
     </Link>
-    <PostBuildEvent>
-      <Command>set PATH=$(Path)
-mkdir $(ToolchainName)
-python.exe "$(VSNaClSDKRoot)\tools\create_nmf.py" -D "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\bin\x86_64-nacl-objdump.exe" -L "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\x86_64-nacl\lib" -L "$(VSNaClSDKRoot)\toolchain\win_x86_$(ToolchainName)\x86_64-nacl\lib32" -o $(ToolchainName)\hello_world_gles.nmf -t $(ToolchainName) -s $(ToolchainName) "$(TargetPath)"</Command>
-      <Message>Running 'create_nmf'</Message>
-    </PostBuildEvent>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|NaCl64'">
     <Link>
diff --git a/NaCl.Build.CPPTasks/NaClLink.cs b/NaCl.Build.CPPTasks/NaClLink.cs
index c623a93..5d09a79 100644
--- a/NaCl.Build.CPPTasks/NaClLink.cs
+++ b/NaCl.Build.CPPTasks/NaClLink.cs
@@ -19,30 +19,42 @@
         /// Property set only in PNaCl builds to signal that the translator
         /// should be run post-link.
         /// </summary>
-        public string TranslateARM { get; set; }
+        public bool TranslateARM { get; set; }
 
         /// <summary>
         /// Property set only in PNaCl builds to signal that the translator
         /// should be run post-link.
         /// </summary>
-        public string TranslateX86 { get; set; }
+        public bool TranslateX86 { get; set; }
 
         /// <summary>
         /// Property set only in PNaCl builds to signal that the translator
         /// should be run post-link.
         /// </summary>
-        public string TranslateX64 { get; set; }
+        public bool TranslateX64 { get; set; }
 
         [Required]
-        public string OutputCommandLine { get; set; }
+        public bool OutputCommandLine { get; set; }
+
+        [Required]
+        public bool CreateNMF { get; set; }
 
         [Required]
         public string NaClLinkerPath { get; set; }
 
         [Required]
+        public string ProjectName { get; set; }
+
+        [Required]
+        public string ToolchainName { get; set; }
+
+        [Required]
         public string Platform { get; set; }
 
         [Required]
+        public string CreateNMFPath { get; set; }
+
+        [Required]
         public virtual string OutputFile { get; set; }
 
         [Required]
@@ -146,18 +158,26 @@
             return responseFileCmds.ToString();
         }
 
-        private bool Translate(string arch)
+        private static string PexeToNexe(string pexe, string arch)
         {
-            string nexeBase = Path.GetFileNameWithoutExtension(OutputFile) + "_" + arch + ".nexe";
-            string outfile = Path.Combine(Path.GetDirectoryName(OutputFile), nexeBase);
+            string basename = Path.GetFileNameWithoutExtension(pexe) + "_" + arch + ".nexe";
+            return Path.Combine(Path.GetDirectoryName(pexe), basename);
+        }
 
-            string commandLineCommands = String.Format("-arch {0} \"{1}\" -o \"{2}\"", arch, OutputFile, outfile);
+        private bool Translate(string arch, string pnacl_arch=null)
+        {
+            if (pnacl_arch == null)
+                pnacl_arch = arch;
+            string outfile = PexeToNexe(OutputFile, arch);
+            string cmd = String.Format("-arch {0} \"{1}\" -o \"{2}\"",
+                                       pnacl_arch, OutputFile, outfile);
 
-            string translateTool = Path.Combine(Path.GetDirectoryName(GenerateFullPathToTool()), "pnacl-translate.bat");
-            if (OutputCommandLine != "true")
-                Log.LogMessage("pnacl-translate {0}", Path.GetFileName(nexeBase));
+            string dirname = Path.GetDirectoryName(GenerateFullPathToTool());
+            string translateTool = Path.Combine(dirname, "pnacl-translate.bat");
+            if (!OutputCommandLine)
+                Log.LogMessage("pnacl-translate {0}", Path.GetFileName(outfile));
 
-            if (ExecuteTool(translateTool, commandLineCommands, string.Empty) != 0)
+            if (ExecuteTool(translateTool, cmd, string.Empty) != 0)
             {
                 return false;
             }
@@ -165,9 +185,14 @@
             return true;
         }
 
+        private bool IsPNaCl()
+        {
+            return Platform.Equals("pnacl", StringComparison.OrdinalIgnoreCase);
+        }
+
         public override bool Execute()
         {
-            if (Platform.Equals("pnacl", StringComparison.OrdinalIgnoreCase))
+            if (IsPNaCl())
             {
                 if (!SDKUtilities.FindPython())
                 {
@@ -176,37 +201,99 @@
                 }
             }
 
-            bool returnResult = false;
+            xamlParser = new XamlParser(PropertiesFile);
+            if (!Setup())
+                return false;
 
-            try
+            if (!OutputCommandLine)
+                Log.LogMessage("Linking: {0}", Path.GetFileName(OutputFile));
+
+            if (!base.Execute())
+                return false;
+
+            if (!PostLink())
+                return false;
+
+            return true;
+        }
+
+        protected bool PostLink()
+        {
+            if (IsPNaCl())
             {
-                xamlParser = new XamlParser(PropertiesFile);
-                if (!Setup())
-                    return false;
-                returnResult = base.Execute();
-
-                if (TranslateX64 == "true" && !Translate("x86_64"))
+                if (TranslateX64 && !Translate("64", "x86-64"))
                     return false;
 
-                if (TranslateX86 == "true" && !Translate("i686"))
+                if (TranslateX86 && !Translate("32", "i686"))
                     return false;
 
-                if (TranslateARM == "true" && !Translate("arm"))
+                if (TranslateARM && !Translate("arm"))
                     return false;
             }
-            finally
-            {
 
+            if (CreateNMF)
+            {
+                if (!SDKUtilities.FindPython())
+                {
+                    Log.LogError("Automatic NMF creation requires python in your executable path.");
+                    return false;
+                }
+
+                string outputRoot = ToolchainName;
+                if (IsPNaCl())
+                    outputRoot = "PNaCl";
+
+                if (!Directory.Exists(outputRoot))
+                    Directory.CreateDirectory(outputRoot);
+
+                string nmfPath = Path.Combine(outputRoot, Path.ChangeExtension(ProjectName, ".nmf"));
+
+                string cmd = "\"" + CreateNMFPath + "\" -o \"" + nmfPath + "\"";
+                cmd += " -t " + ToolchainName + " -s " + ToolchainName;
+
+                if (IsPNaCl())
+                {
+                    if (!TranslateARM && !TranslateX64 && !TranslateX86)
+                        // Don't run create_nmf unless we actaully produced a nexe file.
+                        return true;
+
+                    foreach (var arch in new string []{ "arm", "32", "64" })
+                    {
+                        string nexe = PexeToNexe(OutputFile, arch);
+                        if (File.Exists(nexe))
+                            cmd += " \"" + nexe + "\"";
+                    }
+                }
+                else
+                {
+                    if (ToolchainName == "glibc")
+                    {
+                        string bindir = Path.GetDirectoryName(NaClLinkerPath);
+                        string tcroot = Path.GetDirectoryName(bindir);
+                        cmd += " -D \"" + Path.Combine(bindir, "x86_64-nacl-objdump.exe") + "\"";
+                        cmd += " -L \"" + Path.Combine(tcroot, "x86_64-nacl", "lib") + "\"";
+                        cmd += " -L \"" + Path.Combine(tcroot, "x86_64-nacl", "lib32") + "\"";
+                    }
+                    cmd += " \"" + OutputFile + "\"";
+                }
+
+                if (!OutputCommandLine)
+                    Log.LogMessage("CreateNMF");
+
+                if (ExecuteTool("python", string.Empty, cmd) != 0)
+                {
+                    return false;
+                }
             }
 
-            return returnResult;
+            return true;
         }
 
         protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands)
         {
-            if (OutputCommandLine == "true")
+            if (OutputCommandLine)
             {
-                Log.LogMessage(MessageImportance.High, pathToTool + "  " + responseFileCommands);
+                Log.LogMessage(MessageImportance.High, pathToTool + "  " + responseFileCommands + " " + commandLineCommands);
             }
 
             return base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands);
diff --git a/NaCl.Build.CPPTasks/NaClToolTask.cs b/NaCl.Build.CPPTasks/NaClToolTask.cs
index 8ce87c0..0b204af 100644
--- a/NaCl.Build.CPPTasks/NaClToolTask.cs
+++ b/NaCl.Build.CPPTasks/NaClToolTask.cs
@@ -30,7 +30,7 @@
         private bool skippedExecution;
         private bool minimalRebuildFromTracking;
         private bool trackFileAccess;
-        protected ITaskItem[] compileSourceList;

+        protected ITaskItem[] compileSourceList;
         protected XamlParser xamlParser;
 
         protected abstract CanonicalTrackedOutputFiles OutputWriteTLog(ITaskItem[] compiledSources);
@@ -43,6 +43,14 @@
         [Required]
         public virtual ITaskItem[] Sources { get; set; }
 
+
+        // Override default StandardOutputLoggingImportance so that we see the stdout from the
+        // toolchain from within visual studio.
+        protected override MessageImportance StandardOutputLoggingImportance
+        {
+            get { return MessageImportance.Normal; }
+        }
+
         protected bool ForcedRebuildRequired()
         {
             string tlogCommandPath = null;
diff --git a/TestingProjects/BlankValidSolution/NaClProject/nacl_project.nmf b/TestingProjects/BlankValidSolution/NaClProject/nacl_project.nmf
deleted file mode 100644
index 7082c4a..0000000
--- a/TestingProjects/BlankValidSolution/NaClProject/nacl_project.nmf
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "program": {
-    "x86-64": {
-      "url": "newlib/NaClProject.nexe"
-    },
-    "x86-32": {
-      "url": "newlib/NaClProject.nexe"
-    }
-  }
-}
diff --git a/build.bat b/build.bat
index c4b6f63..c72b67a 100755
--- a/build.bat
+++ b/build.bat
@@ -1,9 +1,14 @@
 @echo off
 setlocal
 
+set BUILD_ERRORLEVEL=
 :: Set up the Visual Studio environment
 call "%VS100COMNTOOLS%vsvars32.bat"
 msbuild "%~dp0NativeClientVSAddIn.sln"
+if %ERRORLEVEL% NEQ 0 goto endbuild
 python "%~dp0create_package.py"
 
-endlocal
+endlocal & set BUILD_ERRORLEVEL=%ERRORLEVEL%
+
+:endbuild
+exit /B %BUILD_ERRORLEVEL%