gojimmypi / VerilogLanguageExtension

Verilog Language Extension for Visual Studio
https://marketplace.visualstudio.com/items?itemName=gojimmypi.gojimmypi-verilog-language-extension
MIT License
18 stars 3 forks source link

Added Project Template for Verilog #25

Closed BarryNolte closed 1 year ago

BarryNolte commented 4 years ago

Here's a project template for Verilog. It will create a new project from New/Project... and setup 'build/rebuild/clean' from the menu. Deploy could become part of the build step, but is made difficult by VS because it wants to deploy through the remote debugger, and since that doesn't run on the FPGA, deploy is a foreign concept. I'm sure if you're going to add menu items, then running an arbitrary task from MSBuild (add to verilog.csproj) can't be that hard. To use, build, then drop the resultant .zip file into your Templates\ProjectTemplates directory and you should have a Verilog project type. Let me know if there's anything else you need.

gojimmypi commented 4 years ago

@BarryNolte thank you again for the template idea... I continue to run with that concept, but one thing that I've found is when calling a batch file to do a build, the entire process seems to run to completion before any output is sent to the Visual Studio build console. Instead, there's a long delay while messages are being generated during build, then everything is burped out all at once.

Is there a way to have Visual Studio display text as it is generated? I've tried a variety of methods in both DOS and WSL to spawn new processes, but all seem to have the same result.

or

%comspec% /k %VBUILDCMD% $(make -f %VBUILDMAKE% %VBUILDTARGET%)
BarryNolte commented 4 years ago

I bet you're running that on the UI thread and it's blocking updates. Try spinning up another thread or task to run the batch file so the UI can update.

gojimmypi commented 4 years ago

I've confirmed that I can have multiple processes running concurrently; Two ping commands start, output overlapping, and finish at about the same time. Are they running in separate threads? That's a different story.

For instance, I tried this in the vs-prog.bat:

echo "Calling %VBUILDCMD% (make prog -f %VBUILDMAKE% %VBUILDTARGET%) & ..."
echo "%SystemRoot%"

start "Build Ping 1" /b ping -n 5 192.168.1.1

start "Build Ping 2" /b ping -n 5  192.168.1.2
echo "Version %3"

with this output:

1>------ Build started: Project: Verilog44, Configuration: iCEBreaker Deploy Any CPU ------
1>  "Calling "C:\WINDOWS\Sysnative\wsl.exe" (make prog -f Makefile-iCEBreaker.mk ) & ..."
1>  "C:\WINDOWS"
1>  "Version 3"
1>  Done!
1>  
1>  Pinging 192.168.1.1 with 32 bytes of data:
1>  
1>  Pinging 192.168.1.2 with 32 bytes of data:
1>  Reply from 192.168.1.108: Destination host unreachable.
1>  Reply from 192.168.1.108: Destination host unreachable.
1>  Reply from 192.168.1.108: Destination host unreachable.
1>  Reply from 192.168.1.108: Destination host unreachable.
1>  Reply from 192.168.1.108: Destination host unreachable.
1>  Reply from 192.168.1.108: Destination host unreachable.
1>  Reply from 192.168.1.108: Destination host unreachable.
1>  Reply from 192.168.1.108: Destination host unreachable.
1>  Reply from 192.168.1.108: Destination host unreachable.
1>  
1>  Ping statistics for 192.168.1.2:
1>      Packets: Sent = 5, Received = 5, Lost = 0 (0% loss),
1>  Reply from 192.168.1.108: Destination host unreachable.
1>  
1>  Ping statistics for 192.168.1.1:
1>      Packets: Sent = 5, Received = 5, Lost = 0 (0% loss),
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

but that, too - gets all burped out to the console only after completion.

Apparently there's a new powershell feature in Version 7 called Start-ThreadJob. I of course don't have that version installed, and I'm not sure I want end users to have to manually install a custom powershell version just for this.

I've also experimented with some of the Exec task parameters, with no luck.

Here's my exec:

  <Target Name="Build">
    <Message Text="Building Verilog Files" />
    <MakeDir Directories="$(OutputPath)" Condition="!Exists('$(OutputPath)')" />

    <Exec Command=".\build\vs-prog.bat iCEBreaker Makefile-iCEBreaker.mk 3" YieldDuringToolExecution="true" Condition="'$(Configuration)'=='iCEBreaker Deploy' ">
    </Exec>

  </Target>

Edit: I also tried this exec with a start there:

  <Target Name="Build">
    <Message Text="Building Verilog Files" />
    <MakeDir Directories="$(OutputPath)" Condition="!Exists('$(OutputPath)')" />

    <Exec Command="start /b .\build\vs-prog.bat iCEBreaker Makefile-iCEBreaker.mk" ConsoleToMsBuild="true" Condition="'$(Configuration)'=='iCEBreaker Deploy' " />
  </Target>

Would you happen to have any tips on the thread idea?

BarryNolte commented 4 years ago

I’ll look in the morning, I have a few ideas.

Get Outlook for iOShttps://aka.ms/o0ukef


From: gojimmypi notifications@github.com Sent: Sunday, June 7, 2020 5:50:33 PM To: gojimmypi/VerilogLanguageExtension VerilogLanguageExtension@noreply.github.com Cc: Barry Nolte barry@barrynolte.com; Mention mention@noreply.github.com Subject: Re: [gojimmypi/VerilogLanguageExtension] Added Project Template for Verilog (#25)

I've confirmed that I can have multiple processes running concurrently; Two ping commands start, output overlapping, and finish at about the same time. Are they running in separate threads? That's a different story.

For instance, I tried this in the vs-prog.bat:

echo "Calling %VBUILDCMD% (make prog -f %VBUILDMAKE% %VBUILDTARGET%) & ..." echo "%SystemRoot%"

start "Build Ping 1" /b ping -n 5 192.168.1.1

start "Build Ping 2" /b ping -n 5 192.168.1.2 echo "Version %3"

with this output:

1>------ Build started: Project: Verilog44, Configuration: iCEBreaker Deploy Any CPU ------ 1> "Calling "C:\WINDOWS\Sysnative\wsl.exe" (make prog -f Makefile-iCEBreaker.mk ) & ..." 1> "C:\WINDOWS" 1> "Version 3" 1> Done! 1> 1> Pinging 192.168.1.1 with 32 bytes of data: 1> 1> Pinging 192.168.1.2 with 32 bytes of data: 1> Reply from 192.168.1.108: Destination host unreachable. 1> Reply from 192.168.1.108: Destination host unreachable. 1> Reply from 192.168.1.108: Destination host unreachable. 1> Reply from 192.168.1.108: Destination host unreachable. 1> Reply from 192.168.1.108: Destination host unreachable. 1> Reply from 192.168.1.108: Destination host unreachable. 1> Reply from 192.168.1.108: Destination host unreachable. 1> Reply from 192.168.1.108: Destination host unreachable. 1> Reply from 192.168.1.108: Destination host unreachable. 1> 1> Ping statistics for 192.168.1.2: 1> Packets: Sent = 5, Received = 5, Lost = 0 (0% loss), 1> Reply from 192.168.1.108: Destination host unreachable. 1> 1> Ping statistics for 192.168.1.1: 1> Packets: Sent = 5, Received = 5, Lost = 0 (0% loss), ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

but that, too - gets all burped out to the console only after completion.

Apparently there's a new powershell feature in Version 7https://devblogs.microsoft.com/powershell/announcing-powershell-7-0/ called Start-ThreadJobhttps://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_thread_jobs?view=powershell-7&viewFallbackFrom=powershell-5.1. I of course don't have that version installed, and I'm not sure I want end users to have to manually install a custom powershell version just for this.

I've also experimented with some of the Exec taskhttps://docs.microsoft.com/en-us/visualstudio/msbuild/exec-task?view=vs-2019 parameters, with no luck.

Here's my exec:

Would you happen to have any tips on the thread idea?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/gojimmypi/VerilogLanguageExtension/pull/25#issuecomment-640307560, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ABCEKPNPVOKDBWA55B4QNP3RVQYVTANCNFSM4MZ537GQ.

gojimmypi commented 4 years ago

I thought the most promising idea was the ExecAsync using inline include code from and idea on StackOverflow that I've adapted here:

  <PropertyGroup>
    <InstallBuildDependenciesCmd>.\build\vs-prog.bat</InstallBuildDependenciesCmd>
    <PipelineDependsOn>
      InstallBuildDependencies;
    </PipelineDependsOn>
</PropertyGroup>

  <!--Launch a Process in Parallel
  https://stackoverflow.com/questions/2387456/msbuild-exec-task-without-blocking
  -->
  <UsingTask TaskName="ExecAsync" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
    <ParameterGroup>
      <!--The file path is the full path to the executable file to run-->
      <FilePath ParameterType="System.String" Required="true" />
      <!--The arguments should contain all the command line arguments that need to be sent to the application-->
      <Arguments ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
      <Code Type="Fragment" Language="cs">
        <![CDATA[
  // https://stackoverflow.com/questions/2387456/msbuild-exec-task-without-blocking      

     Log.LogMessage("0");
     Console.WriteLine("Hello World");
using (System.Diagnostics.Process compiler = new System.Diagnostics.Process())
{
    compiler.StartInfo.FileName = "C:\\Users\\gojimmypi\\source\\repos\\Verilog45\\Verilog45\\build\\vs-prog.bat";
    compiler.StartInfo.Arguments = "iCEBreaker Makefile-iCEBreaker.mk 4";

    compiler.StartInfo.FileName = "ping.exe";
    compiler.StartInfo.Arguments = "192.168.1.1";

    string eOut = null;    
    compiler.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler((sender, e) => 
                                 { eOut += e.Data; });
    compiler.StartInfo.UseShellExecute = false;
    compiler.StartInfo.RedirectStandardOutput = true;
    compiler.StartInfo.RedirectStandardError = true;
    compiler.Start();
    //System.Diagnostics.Process.Start(compiler.StartInfo);

    // https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo.redirectstandardoutput?view=netcore-3.1#System_Diagnostics_ProcessStartInfo_RedirectStandardOutput
    System.IO.StreamReader reader = compiler.StandardOutput;
    compiler.BeginErrorReadLine();
    Log.LogMessage("1");
                string line;
                // Read and display lines from the file until the end of
                // the file is reached.
                while ((line = reader.ReadLine()) != null)
                {
                  Log.LogMessage("Starting {0}...", line);  
                    Console.WriteLine(line);
                }
    // Console.WriteLine(compiler.StandardOutput.ReadToEnd());
     Log.LogMessage("2");

     compiler.WaitForExit();
     Log.LogMessage("3");
}

  ]]>
      </Code>
    </Task>
  </UsingTask>

and use it like this:

    <ExecAsync FilePath='$(InstallBuildDependenciesCmd)' Arguments='iCEBreaker Makefile-iCEBreaker.mk 4'   Condition="'$(Configuration)'=='iCEBreaker Deploy' " />

You'll note that I'm even trying explicit Log.LogMessage, reading one line at a time.

If I do not have:

    compiler.StartInfo.UseShellExecute = false;
    compiler.StartInfo.RedirectStandardOutput = true;
    compiler.StartInfo.RedirectStandardError = true;

Then a DOS window pops up during build, I do see each output message as it occurs. But If I put those three lines back: UseShellExecute = false and RedirectStandardOutput = true I see the same problem where things execute until completion, and then burp out all the messages at once in the 'Output` windows in Visual Studio.

Note to see any build at all in the Output window, I needed to turn up the verbosity here:

image

There's this interesting Tools-Options setting that does not "stick". I click it, but when returning it is unchecked:

image

Alas this ExecAsync idea too, is a process and not a thread.

gojimmypi commented 4 years ago

I had also hoped that exec-task UseCommandProcessor="true" might be a solution, but it has other problems

BarryNolte commented 4 years ago

I'm looking at this now. This is a weird one. I thought you were creating the process from code, so my separate thread idea is probably invalid. I can reproduce this, so I going to dig a little deeper. There's got to be something simple here that we're missing. If I run the project directly from MSBuild, it gives program spew at is goes, so it appears to be in VS somewhere. I found this along the way: http://reedbeta.com/blog/custom-toolchain-with-msbuild/ I might have to tighten up my project template a bit :)

BarryNolte commented 4 years ago

OK, I'm convinced that this is the way it works. I'm not sure why. I'd like to try to write some code that runs the 'build' process and updates the output window itself. Which branch should I use?

gojimmypi commented 4 years ago

I'm pushed all the latest code to the development-interim branch. I have the most recent sample project that I've been manually fussing with, as well.

Thanks for taking a look at this. I'm quite curious as to what you might find.

BarryNolte commented 4 years ago

When I run, I get a bunch of ignorable exceptions from VS, then a "Cannot find the requested resource: 'Menus.ctmenu'." which is unrecoverable. Is there a missing file?

gojimmypi commented 4 years ago

hm. I have nothing called ctmenu. Nor do I believe I am making any menu adjustments (but at some point, I would like to learn how to do this, too)

I did a fresh clone to a test directory, and opened the solution file there. I used Visual Studio to switch to the development-interim branch. Then clicked the green arrow "Current Instance" launch button.

Select Create a New Project; Verilog, Next.... Press Create...

Select "Icebreaker Deploy" build option and it does the ping routine (running to completion before all results shown).

No errors observed. I don't think I have any external dependencies. Do you have a prior version of the Verilog Syntax Highlighter already installed?

Target Framework: .NET Framework 4.5 (does this version of SDK need to be installed?)

Microsoft Visual Studio Enterprise 2019
Version 16.6.1
VisualStudio.16.Release/16.6.1+30128.74
Microsoft .NET Framework
Version 4.8.03752

Installed Version: Enterprise
BarryNolte commented 4 years ago

Well... I give up. In all my testing, it appears that it spools up all the output from a tool and then splats it out at process exit. It will do this for c/cpp and c# projects also. I thought for sure the VC guys were cheating somehow (I even decompiled there build task dll). I would have sworn that it use to interleave output from multiple processes as things built. I'm betting this is a 'feature' having to do with performance. I found some evidence of using Exec in msbuild is not for long running processes. I'm not sure what to do with a long running process.

gojimmypi commented 4 years ago

That's just too weird. I can't believe that's the case. But I too spent hours on a variety of solutions all with the same result.

Do you think this is worth opening an issue with VSBuild? Surely we are not the first to want to do this.

I do have a hacky, ugly alternative: let a DOS window pop up while the synthesis is running. Seems to be the only choice.... or just sit and wait in silence until completion.

Thanks for taking a look

BarryNolte commented 4 years ago

I found a question on the MSFT forums that points to this problem in 2012 and the support person wouldn't say it was a problem. So I think the options are:

  1. Keep it as it is. (clunky, but works)
  2. Just launch a command window as noted above.
  3. Try to run it in the terminal window, so at least you can see it's progress (no jump to source on error).
  4. Run it through CreateProcess, take the stdio/stderr output and pass it to the output window as it comes in. (Not clunky, works as expected, but requires some coding)
  5. Create your own toolwindow and pipe the output to it like in #4 (Not clunky, requires much more coding, but creates a window for you're 'brand' :) )

I'd lean towards #4 with #3 a close second, but that's my $0.02 cents worth.

gojimmypi commented 4 years ago

thanks so much for taking a look at this! Regarding "Run it through CreateProcess", that's essentially the C# Process.Start that I tried above, no? I'm even doing it line by line with Log.LogMessage:

using (System.Diagnostics.Process compiler = new System.Diagnostics.Process())
{
  System.IO.StreamReader reader = compiler.StandardOutput;

  compiler.BeginErrorReadLine();

  while ((line = reader.ReadLine()) != null)
  {
    Log.LogMessage("Starting {0}...", line);  
    Console.WriteLine(line);
  }
BarryNolte commented 4 years ago

Yes, exactly, but that code needs a bit of extra work. That code runs in msbuild.exe, not in devenv.exe, so the Log.LogMessage willl output to the msbulid log, which is where all this breaks down. If you create the process from devenv (in your package), then grab the output from the stream, then you can update the output window at will as the build process runs. There must be a build event to hook into to start this all off.

gojimmypi commented 4 years ago

the good thing is that I've figured out how to create custom commands in Visual Studio, running from the devenv.exe process. The bad thing is that it does not output to the build window.

see VSIXCustomToolbarCommand

gojimmypi commented 4 years ago

I have another sample here

In particular, this line

@BarryNolte Any ideas how to set SynchronizingObject ? (this is of a different type)

BarryNolte commented 4 years ago

Sorry for the delay, I mailed back my response 3 days ago and it didn't get posted here, checked back in today and it didn't see it.

OK, if I read the docs right, the SynchronizingObject if set to a UI control, will force the exit event to run on the UI thread instead of a random thread out of the thread pool. As long as you don’t touch the UI on the exit event, then it doesn’t matter.

Now, for not getting any output. Well, I’m not sure why, but here’s a catch… this code (Command1:Execute) will suffer the same problem as before, sort of… It’s all running on the UI thread, blocking, while the compiler is running. You’ll have to run this in a separate thread or task. VS is very good at two things, silently eating your mistakes, or failing spectacularly at your mistakes, I think you’ve hit the former.

Aha…

// this next line will freeze the IDE until process compiler completes // compiler.WaitForExit();

This is the clue, I think everything is getting eaten by VS up until this point, since the UI can’t run, then when it can, there’s nothing left to do, thus, not output. Time for some fancy asynchronous programming 😊.

ThomasArdal commented 4 years ago

Back in the day, I would have asked @sayedihashimi. It's kind of a long shot, but maybe he knows something?

sayedihashimi commented 4 years ago

I'm really not sure, it's been quite some time since I've done anything significant with MSBuild customizations.

gojimmypi commented 4 years ago

@BarryNolte - check out this!

BarryNolte commented 4 years ago

I'd ask why, but I stopped doing that a long time ago... :) Someday, when I'm really bored, I'll maybe, try to figure out why. Since it works with MSBuild on it's own, that must put VS in some other mode when it executes msbuild. Very cool that MS got back to you!

gojimmypi commented 4 years ago

yes, without the help of you and some clever Microsoft folks, I would have never figured this out.

Check out my latest comments in https://github.com/microsoft/msbuild/issues/5486

The cool thing, is I have my Orange Crab building in Visual Studio today for the first time!

btw, I am keeping all the things I learned about msbuild tasks in my msbuildCustomTask repo.