Archives
-
Persisting custom properties on a Visual Studio project using object model
It is possible to persist custom properties in your Visual Studio project. I will show how using some PowerShell code in the NuGet console.
Note that VariableValue() persist in session, the call to VariablePersists() writes to the project file for persistance over sessions.
The Global object has the following methods:
PM> (Get-Project).Globals | gm TypeName: System.__ComObject#{e68a3e0e-b435-4dde-86b7-f5adefc19df2} Name MemberType Definition ---- ---------- ---------- VariableExists ParameterizedProperty bool VariableExists (string) {get} VariablePersists ParameterizedProperty bool VariablePersists (string) {get} {set} VariableValue ParameterizedProperty Variant VariableValue (string) {get} {set} DTE Property DTE DTE () {get} Parent Property IDispatch Parent () {get} VariableNames Property Variant VariableNames () {get}
Some sample code:
PM> (Get-Project –name "MyProject").Globals.VariableValue("FirstProperty") = "Value1" PM> (Get-Project –name "MyProject").Globals.VariableValue("SecondProperty") = "Value2" PM> (Get-Project –name "MyProject").Globals.VariablePersists("FirstProperty") = $True PM> (Get-Project –name "MyProject").Globals.VariablePersists("SecondProperty") = $True PM> (Get-Project –name "MyProject").Globals.VariableValue("FirstProperty") Value1 PM> (Get-Project –name "MyProject").Globals.VariablePersists("SecondProperty") True
PM> (Get-Project –name "MyProject").Globals.VariableNames FirstProperty SecondProperty
PM> (Get-Project –name "MyProject").Globals.VariableExists("DoesNotExist") False PM> (Get-Project –name "MyProject").Globals.VariableExists("FirstProperty") True
And if you look at your .csproj file:
<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> :
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\SharePointTools\Microsoft.VisualStudio.SharePoint.targets" /> <ProjectExtensions> <VisualStudio> <UserProperties SecondProperty="Value2" FirstProperty="Value1" /> </VisualStudio> </ProjectExtensions> </Project>
-
Set the properties on your Visual Studio project
Reminder to self:
Settings properties on a Visual Studio project,like DefaultNamespace and AssemblyName.
In NuGet console window:
(Get-Project).Properties.Item("DefaultNamespace").Value = "HelloWorld"
Available properties on a simple class library project project:
PM> (Get-Project).Properties | Select-Object -Property Name,Value
Name Value
---- -----TargetFrameworkMoniker .NETFramework,Version=v4.0
ComVisible False
EnableSecurityDebugging True
OptionCompare 0
StartupObject
ManifestCertificateThumbprint
Trademark
Title FactoryExtensionsTemplate
AssemblyOriginatorKeyFileType 1
FileName FactoryExtensions3.csproj
WebServer
AssemblyOriginatorKeyMode 0
AssemblyKeyContainerName
ProjectType 0
ReferencePath
PreBuildEvent
Copyright Copyright © Microsoft 2011
ApplicationIcon
ExcludedPermissions
RunPostBuildEvent 1
DefaultTargetSchema 1
RootNamespace HelloWorld
ManifestTimestampUrl
ManifestKeyFile
DebugSecurityZoneURL
Product FactoryExtensionsTemplate
PostBuildEvent
OptionStrict 0
DefaultHTMLPageLayout 1
DelaySign False
OutputType 2
NeutralResourcesLanguage
OptionExplicit 1
OutputFileName FactoryExtensions.dll
ServerExtensionsVersion
AssemblyGuid 919e5f68-ec7d-4508-aa24-ee3da14edd84
GenerateManifests False
AssemblyVersion 1.0.0.0
Win32ResourceFile
Description
URL file:///d:\temp3\Acme.Portal\FactoryExtensions3\
DefaultClientScript 0
TargetFramework 262144
SignManifests False
OfflineURL
WebServerVersion
Publish System.__ComObject
AssemblyType 0
FullPath d:\temp3\Acme.Portal\FactoryExtensions3\
WebAccessMethod
AssemblyKeyProviderName
TypeComplianceDiagnostics False
Company Microsoft
ActiveFileSharePath
AssemblyOriginatorKeyFile
ApplicationManifest DefaultManifest
AssemblyFileVersion 1.0.0.0
AspnetVersion
FileSharePath
AssemblyName FactoryExtensions
LocalPath d:\temp3\Acme.Portal\FactoryExtensions3\
DefaultNamespace HelloWorld
LinkRepair False
TargetZone
SignAssembly False -
Nuget 1.3: some observations
I’m testing out a lot of features from NuGet for extending our software factory. In this testing I made the following observations:
Tools\Init.ps1 is only executed if the Content folder contains a file[See: http://nuget.codeplex.com/discussions/257282#post611456]- Tools\Install.ps1 is executed AFTER Tools\Init.ps1
- If you have an empty solution without projects and install a package then the Tools\Init.ps1 is executed, but Tools\Install.ps1 is NOT executed
- Tools\Install.ps1 is executed when a package is installed for a specific project
- The $project parameter is always $null in Tools\Init.ps1
- Don’t use the nuget.config file to configure your packages folder, it is an undocumented experimental feature which results in unexpected behaviour
- Packages are installed at the solution level in a folder packages next to your solution file (.sln file)
- Each project that has packages installed writes these installed packages and the installed packages it depends on in a packages.config file that is located in the project folder next to the project file (for example .csproj file)
<?xml version="1.0" encoding="utf-8"?> <packages> <package id="EntityFramework" version="4.1.10331.0" /> <package id="T4Scaffolding" version="1.0.0" /> <package id="MacawSolutionsFactory-Core" version="1.0.0" /> <package id="MacawSolutionsFactory-Spf2010" version="1.0.0" /> </packages>
- The packages folder contains a file repositories.config referencing the packages.config files of all projects in the solution that has packages installed
<?xml version="1.0" encoding="utf-8"?> <repositories> <repository path="..\srcfactory\Core\packages.config" /> <repository path="..\srcfactory\Spf2010\packages.config" /> </repositories>
- The repositories.config and packages.config files are used for “reference counting” packages. If uninstall-package is executed on a package, and there is no more packages.config file containing the package, the package folder is removed from the packages folder
- The parameter $installPath points to the folder where the package is installed
- The parameter $rootPath points to the Tools folder in the folder where the package is installed
- The parameter $package is a package object describing information about the package. To see the values of the properties add the code $package | format-custom –depth 1 to your Init.ps1/Install.ps1/Uninstall.ps1.
- The parameter $project is an EnvDTE Project object ($null in Tools\Init.ps1, this script is executed at the solution level)
- Uninstall-package removes a package, but does not automatically cascade delete the packages that were installed because the package was depending on these packages. Use the parameter –RemoveDependencies to do this
- Scaffolders (see T4Scaffolding package for more info) are only available in the project where the package containing the scaffolders in installed. To have access to the scaffolders in all projects, add the project at the solution level
- A package can’t be installed at the solution level using the install-package command, use the command-line NuGet.exe tool to do this
- A good place for more information on how to create packages, what the parameters mean, etc is http://nuget.codeplex.com/documentation?title=Creating%20a%20Package
- NuGet is a very active open source project with a lot of great discussions on http://nuget.codeplex.com/discussions
You can execute the command Install-Package NuGetPSVariables to validate some of the above observations.
UPDATE 1:
- Great NuGet documentation on http://docs.nuget.org (uses open source MarkDown based documentation system: http://nugetdocs.codeplex.com) [Thanks Cyriel!]
<li><font color="#ff0000">No files required in Content folder for Init.ps1 to run</font> </li> <li><font color="#ff0000">If a project is removed from a solution (and not deleted from the filesystem) the <strong>packages.config</strong> file in the removed project is still referenced in the <strong>packages\repositories.config</strong> file. It is now not possible to remove a package at solution level that is also referenced by the removed project. Delete the removed project from the filesystem to solve this issue.</font> </li> <li><font color="#ff0000">Add the <strong>packages.config</strong> files in your project folders to your Visual Studio project (Show hidden files on project, Include in project) and include in source control. Set the build action to none otherwise the file could be included in the project output [Thanks Marino!]</font> </li> <li><font color="#ff0000">Add the <strong>packages</strong> folder to source control (or at least the <strong>packages\repositories.config</strong> file)</font> </li>
UPDATE 2:
- When a solution is loaded the script Tools\Init.ps1 of all packages is executed. In execution of the Init.ps1 scripts the package dependencies are respected. So if package A depends on package B, the script B\Tools\Init.ps1 is executed before script A\Tools\Init.ps1.
UPDATE 3:
-
When a package is installed, the Tools folder is added to the PATH environment variable ($env:PATH) with process scope, so scripts and applications in the tools folder can be directly executed from the NuGet console. Note that if you uninstall the package, the Tools folder of the uninstalled package in NOT removed from the PATH. When the package is installed again, the Tools folder is added to the again, so it appears twice in the PATH.
NuGet: refreshing NuGet packages in packages folder during development
I'm working on NuGet packages in a Visual Studio project. On build I publish a new version of my nuget package to a repository (a directory on my system).
I don't increment version numbers after each change, i only want to do that on published versions of my packages.
To test out changes to the package in my solution where I already reference the package I want to pull in a new version of the package that has the same version number.
install-package does not have a -force option to force uninstall/install of the package with an unchanged version number.
The script below solves this issue. Save as Update-NuGetPackages.ps1 and put next to NuGet.exe.
Test out first on a copy of your Packages folder (copy to PackagesTest), so you have all parameters right. The script deletes all existing packages.
UPDATE: Updated the code so if package is deleted and install fails, an empty folder with the package name is created so after fixing the error the script can be called again to retry the update.
- <#
- .SYNOPSIS
- Update NuGet packages with same version of package.
- .DESCRIPTION
- During the development cycle version numbers of packages are often not changed,
- but the contents of the package itself is updates. Update-NuGetPackages.ps1
- refreshes all packages in a given packages directory.
- By default don't specify any sources, so NuGet.exe (version 1.3 and later) will use
- the NuGet.config which is stored in the AppData folder and shared between Visual Studio
- and NuGet.exe
- .PARAMETER PackagesPath
- Path to folder containing the packages to refresh.
- .PARAMETER PackagesSource
- Source where packages should be installed from. Post version 1.3 of NuGet.exe
- supports specifying multiple sources separated by ';'.
- Source paths may not contain PSDrives in its path, like product:\mypath.
- .PARAMETER IncludeDefaultSource
- Switch parameter to specify that the default NuGet packages source
- https://go.microsoft.com/fwlink/?LinkID=206669 should be included as a
- packages source. Don't specify this switch and dont use the PackagesSource
- parameter to let NuGet.exe use the sources specified in the NuGet.config.
- This functionality is available after release version 1.3 of NuGet.exe.
- If used together with the PackagesSource parameter a version newer than
- version 1.3 of NuGet.exe is required.
- .EXAMPLE
- PS C:\> Update-NuGetPackages.ps1 -PackagesSource c:\MyProduct\Packages
- Deleting package 'EntityFramework' version '4.1.10331.0'
- Deleting package 'MacawSolutionsFactory-Core' version '1.0.0'
- Deleting package 'MacawSolutionsFactory-Spf2010' version '1.0.0'
- Deleting package 'T4Scaffolding' version '1.0.0'
- Reinstalling package 'EntityFramework' version '4.1.10331.0'
- Successfully installed 'EntityFramework 4.1.10331.0'.
- Reinstalling package 'MacawSolutionsFactory-Core' version '1.0.0'
- 'T4Scaffolding (= 1.0.0)' not installed. Attempting to retrieve dependency from source...
- Done.
- Dependency 'EntityFramework (= 4.1.10311.0)' already installed.
- Successfully installed 'T4Scaffolding 1.0.0'.
- Successfully installed 'MacawSolutionsFactory-Core 1.0.0'.
- Reinstalling package 'MacawSolutionsFactory-Spf2010' version '1.0.0'
- Dependency 'T4Scaffolding (= 1.0.0)' already installed.
- Dependency 'EntityFramework (= 4.1.10311.0)' already installed.
- Successfully installed 'MacawSolutionsFactory-Spf2010 1.0.0'.
- Reinstalling package 'T4Scaffolding' version '1.0.0'
- Dependency 'EntityFramework (= 4.1.10311.0)' already installed.
- 'T4Scaffolding 1.0.0' already installed.
- Done.
- .EXAMPLE
- PS C:\> Update-NuGetPackages.ps1 -PackagesSource c:\MyProduct\Packages `
- -PackagesSource "c:\Dist\Feed1;c:\Dist\Feed2" -IncludeDefaultSource
- :
- .INPUTS
- System.String,System.String,System.Boolean
- .NOTES
- This script must be located next to NuGet.exe.
- This script requires a version later than the release version 1.3 of NuGet.exe
- when specifying multiple sources on the commandline and/or using the IncludeDefaultSource
- parameter.
- Version: 1.0
- Author: Serge van den Oever (http://weblogs.asp.net/soever)
- #>
- [CmdletBinding()]
- param
- (
- [Parameter(Position=0, Mandatory=$true)][String]$PackagesPath,
- [Parameter(Position=1, Mandatory=$false)][String]$PackagesSource = '',
- [Parameter(Position=2, Mandatory=$false)][Switch]$IncludeDefaultSource = $false
- )
- $nugetDefaultSource= 'https://go.microsoft.com/fwlink/?LinkID=206669'
- function Get-ScriptDirectory
- {
- $Invocation = (Get-Variable MyInvocation -Scope 1).Value
- Split-Path $Invocation.MyCommand.Path
- }
- $nugetExe = Join-Path -Path (Get-ScriptDirectory) -ChildPath 'nuget.exe'
- if (-not (Test-Path -Path $nugetExe)) { throw ("Expected NuGet.exe at path '{0}'" -f $nugetExe) }
- if (-not (Test-Path -Path $PackagesPath)) { throw ("Specified PackagesPath '{0}' does not exist" -f $PackagesPath) }
- $PackagesPath = Convert-Path -Path $PackagesPath
- # Collect packages
- $packageFolders = Get-ChildItem -Path $PackagesPath | Where-Object {$_.psIsContainer -eq $true}
- $packages = @()
- if ($packageFolders -ne $null) {
- $packageFolders | ForEach-Object {
- $packageFolder = Convert-Path -Path $_.FullName # Handle case where PSDrive is specified
- $packageFolderName = Split-Path -Path $packageFolder -Leaf
- $parts = $packageFolderName.split('.')
- $packageName = $parts[0]
- $packageVersion = $parts[1..$($parts.length-1)] -join '.'
- $packages += @{
- Name = $packageName;
- Version = $packageVersion;
- Folder = $packageFolder;
- }
- }
- }
- # Delete all package folders
- $packages | ForEach-Object {
- Write-Host ("Deleting package '{0}' version '{1}'" -f $_.Name, $_.Version)
- Remove-Item -Recurse -Force -Path $_.Folder -ErrorAction SilentlyContinue
- if (Test-Path -Path $_.Folder) { Write-Warning ("Can't remove folder '{0}'. Make sure it is not locked." -f $_.Folder) }
- }
- # Reinstall all packages
- $packages | ForEach-Object {
- Write-Host ("Reinstalling package '{0}' version '{1}'" -f $_.Name, $_.Version)
- if ($IncludeDefaultSource) {
- $source = $nugetDefaultSource
- } else {
- $source = ''
- }
- if ($PackagesSource -ne '') {
- if ($source -ne '') { $source += ';' }
- $source += (Convert-Path -Path $PackagesSource)
- }
- if ($source -ne '') {
- & $nugetExe install $_.Name -Version $_.Version -Source $source -OutputDirectory $PackagesPath
- } else {
- & $nugetExe install $_.Name -Version $_.Version -OutputDirectory $PackagesPath
- }
- if (-not (Test-Path -Path $_.Folder)) {
- New-Item -Path $_.Folder -Type Directory -Force
- Write-Warning ("Failed to reinstall package '{0}' version '{1}'. Created dummy directory '$($_.Folder). Fix error and try again." -f $_.Name, $_.Version)
- }
- }
- # Call Init.ps1 on all packages
- $packages | ForEach-Object {
- $initScript = Join-Path -Path $_.Folder -ChildPath 'Tools\Init.ps1'
- if (Test-Path -Path $initScript)
- {
- $rootPath = $_.Folder
- $toolsPath = Join-Path -Path $_.Folder -ChildPath 'Tools'
- $package = Get-Package -Filter $_.Name
- $project = $null
- & $initScript -rootPath $rootPath -toolsPath $toolsPath -package $package -project $project
- }
- }
- Write-Host 'Done.'
NuGet: should you check-in packages into source control?
Interesting discussion at http://nuget.codeplex.com/discussions/230110 about checking in NuGet packages into source control.
NuGet packages can contain more than a simple libraries, it can contain tools that grow quite big as well. Good example in the NUnit package that contains the total NUnit toolset.
Do you want all your binaries into source control, it is already preserved in the NuGet feed. You can always reload the packages from the feed. The required packages in a project are available in the packages.config file that is part of your project and checked into source control.
But what if a package is removed from the feed by the author? That would be bad… on the other hand: in this case shouldn’t you remove a dependency on that package anyway?
What about a build server not connected to the internet and not being able to get packages from the NuGet feed?
After reading the discussion I think I would prefer to not check-in packages into source control…
Just found the following post: https://msmvps.com/blogs/theproblemsolver/archive/2011/04/12/nuget-and-projects-under-source-control.aspx taking the same approach. It describes how you can install all missing packages using the command-line version of nuget.exe with the following command: NuGet install MyProject\packages.config –OutputDirectory Packages
From the documentation:
usage: NuGet install packageId|pathToPackagesConfig [options]
Install a package from a given source. If no source is specified the default feed is used.
Specify the id and optionally the version of the package to install. If a path to a
packages.config file is used instead of an id, all the packages it contains are installed.
options:
Source The location to look for packages.
OutputDirectory Specifies the directory in which packages will be installed
Version The version of the package to install.
ExcludeVersion (x) If set, the destination folder will contain only the package name, not the version number
Help (?) help
NuGet: test if a package is installed
With Get-Package you can get the list of currently installed packages. How can you test if a package is installed?
Get-Package returns list of packages as follows:
Id Version Description -- ------- ----------- EntityFramework 4.1.10331.0 DbContext API and Code First workflow for ADO.NET Entity Framework. MacawSolutionsFactory-Core 1.0.0 Macaw Solutions Factory Base package MacawSolutionsFactory-Spf2010 1.0.0 Macaw Solutions Factory core package for Spf2010 T4Scaffolding 1.0.0 A fast and customizable way to build parts of your .NET application via templates
To test if a package is installed do the following:
(get-package | Select-Object -ExpandProperty ID) -contains 'EntityFramework'
This will return $true or $false.