Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper use of Invoke-Expression?

I've just recently completed my first nightly build script (first significant anything script, really) in powershell. I seem to have things working well, if not yet robustly (I haven't handled significant error-checking yet), but I found myself falling into an idiom around the Invoke-Expression cmdlet, and I'm wondering if I'm using it properly.

Specifically, I use a series of variables to build up command-lines that I will use to build the solution, then run the solution's unit tests. e.g., something like:

$tmpDir = "C:\Users\<myuser>\Development\Autobuild"
$solutionPath=$tmpDir+"\MyProj\MyProj.sln"
$devenv="C:\Program Files (x86)\Microsoft Visual Studio 10.0\common7\ide\devenv"
$releaseProfile="Release"
$releaseCommandLine="`"$devenv`" `"$solutionPath`" /build `"$releaseProfile`""

This works well enough, $releaseCommandLine contains the command line that I want to execute when I'm done. I then execute it via this line:

$output = Invoke-Expression "& $releaseCommandLine"

Is this the proper way to execute a manually-built command line from a powershell script? I thought initially that Invoke-Command would do it, but I must have been doing something wrong because I couldn't get that working at all for half an hour, and I got this working almost immediately.

I've followed this same pattern a few other times in this same script. Is this a best-practice?

like image 552
Greg D Avatar asked Aug 31 '09 03:08

Greg D


2 Answers

Looks fine to me. Only thing I'd change is to use more Powershell features in place of fragile assumptions. E.g.:

  • use Join-Path instead of string concatenation

  • use the Env:\ provider to look up the %programfiles(x86)% dir (or better yet, use the HKML:\ provider to find the path - it's in SOFTWARE\Microsoft\VisualStudio\\InstallDir)

  • when I have to write a string that contains literal doublequotes and variable expansion, I usually fall back to the syntax below. Personal preference, obviously.

    '"{0}" "{1}" /build "{2}"' -f $devenv, $solutionPath, $releaseProfile
    

In some cases I'd be inclined to use Process.Start() so that I could capture the stdout & stderr streams independently (and maybe even control stdin interactively, depending on the application).

PS - the '&' is not strictly necessary.

like image 167
Richard Berg Avatar answered Sep 29 '22 07:09

Richard Berg


I think it is unnecessary to use Invoke-Expression here. I've done this with a lot of build scripts and it usually looks like this:

$vsroot = "$env:ProgramFiles(x86)\Microsoft Visual Studio 9.0"
$devenv = "$vsroot\Common7\IDE\devenv.exe"
$sln = Join-Path <source_root> Source\MyProj\MyProj.sln
& $devenv $sln /build Release

or

& $devenv $sln /build "Release|Any CPU"

Although lately, I have had some troubles with using devenv.exe (mis-behaving add-ins, etc), so now I use msbuild.exe:

$msbuild = 'C:\Windows\Microsoft.NET\Framework\v3.5\MSBuild.exe'
& $msbuild $sln /p:Configuration=Release

Currently MSBuild can handle C#, VB and C++ (invokes vcbuild) but it can't handle solutions with setup & deployment projects in them. However, I have found it to be more reliable than using devenv.exe.

BTW you typically need to invoke other tools (sn.exe, signtool.exe, mt.exe, etc) in a build script that are specific to the version of Visual Studio/.NET you want to build against. So it is usually best to configure your environment variables in the same way that the VS 2008 command prompt does. With the PowerShell Community Extensions installed, you can enable one line in the PSCX profile header to enable this for .NET 3.5/VS 2008 settings:

$Pscx:Preferences["ImportVisualStudioVars"] = $true
like image 44
Keith Hill Avatar answered Sep 29 '22 07:09

Keith Hill