diff --git a/docs/cmdlet-example/NuGet.config b/docs/cmdlet-example/NuGet.config new file mode 100644 index 0000000000..14d776165f --- /dev/null +++ b/docs/cmdlet-example/NuGet.config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/cmdlet-example/README.md b/docs/cmdlet-example/README.md new file mode 100644 index 0000000000..0d56bb7755 --- /dev/null +++ b/docs/cmdlet-example/README.md @@ -0,0 +1,129 @@ +Building a C# Cmdlet +==================== + +This example project demonstrates how to build your own C# cmdlet for +PowerShell. + +Setup +----- + +We use the [.NET Command Line Interface][dotnet-cli] (`dotnet`) to build the +cmdlet library. Install the `dotnet` tool and ensure `dotnet --version` is at +least `1.0.0-rc2`. + +.NET CLI uses a `project.json` file for build specifications: + +```json +{ + "name": "SendGreeting", + "description": "Example C# Cmdlet project", + "version": "1.0.0-*", + + "dependencies": { + "Microsoft.PowerShell.5.ReferenceAssemblies": "1.0.0-*" + }, + + "frameworks": { + "netstandard1.5": { + "imports": [ "net40" ], + "dependencies": { + "Microsoft.NETCore": "5.0.1-*", + "Microsoft.NETCore.Portable.Compatibility": "1.0.1-*" + } + } + } +} +``` + +Note that no source files are specified. .NET CLI automatically will build all +`.cs` files in the project directory. + +Going through this step-by-step: + +- `"name": "SendGreeting"`: Name of the assembly to output (otherwise it + defaults to the name of the containing folder). + +- `"version": "1.0.0-*"`: The wild-card can be replaced using the + `--version-suffix` flag to `dotnet build`. + +- [Microsoft.PowerShell.5.ReferenceAssemblies][powershell]: Contains the SDK + reference assemblies for PowerShell version 5. Targets the `net40` framework. + +- [netstandard1.5][]: The target framework for .NET Core portable libraries. + This is an abstract framework that will work anywhere its dependencies work. + +- `"imports": [ "net4" ]`: Since the PowerShell reference assemblies target the + older `net40` framework, we `import` it here to tell `dotnet restore` that we + know we're loading a possibly-incompatible package. + +- [Microsoft.NETCore][netcore]: Provides a set of packages that can be used when + building portable libraries on .NETCore based platforms. + +- [Microsoft.NETCore.Portable.Compatibility][portable]: Enables compatibility + with portable libraries targeting previous .NET releases like .NET Framework + 4.0. Required to build against the PowerShell reference assemblies package. + +Other dependencies can be added as needed; refer to the +[.NET Core package gallery][myget] for package availability, name, and version +information. + +Because the .NET Core packages are not yet released to NuGet.org, you also need +this `NuGet.config` file to setup the [.NET Core MyGet feed][myget]: + +```xml + + + + + + + + +``` + +[dotnet-cli]: https://github.com/dotnet/cli#new-to-net-cli +[powershell]: https://www.nuget.org/packages/Microsoft.PowerShell.5.ReferenceAssemblies +[netstandard1.5]: https://github.com/dotnet/corefx/blob/master/Documentation/architecture/net-standard-applications.md +[netcore]: https://dotnet.myget.org/feed/dotnet-core/package/nuget/Microsoft.NETCore +[portable]: https://dotnet.myget.org/feed/dotnet-core/package/nuget/Microsoft.NETCore.Portable.Compatibility +[myget]: https://dotnet.myget.org/gallery/dotnet-core + +Building +-------- + +.NET Core is a package-based platform, so the correct dependencies first need to +be resolved: + +``` +dotnet restore +``` + +This reads the `project.json` and `NuGet.config` files and uses NuGet to restore +the necessary packages. The generated `project.lock.json` lockfile contains the +resolved dependency graph. + +Once packages are restored, building is simple: + +``` +dotnet build +``` + +This will produce the assembly `./bin/Debug/netstandard1.5/SendGreeting.dll`. + +This build/restore process should work anywhere .NET Core works, including +Windows, Linux, and OS X. + +Deployment +---------- + +In PowerShell, check `$env:PSMODULEPATH` and install the new cmdlet in its own +module folder, such as, on Linux, +`~/.powershell/Modules/SendGreeting/SendGreeting.dll`. + +Then import and use the module: + +```powershell +> Import-Module SendGreeting # Module names are case-sensitive on Linux +> Send-Greeting -Name World +Hello World! +``` diff --git a/docs/cmdlet-example/SendGreeting.Tests.ps1 b/docs/cmdlet-example/SendGreeting.Tests.ps1 new file mode 100644 index 0000000000..2537d3fa60 --- /dev/null +++ b/docs/cmdlet-example/SendGreeting.Tests.ps1 @@ -0,0 +1,13 @@ +Describe "Send-Greeting cmdlet" { + It "Should be able build the cmdlet" { + Remove-Item -Recurse -Force bin -ErrorAction SilentlyContinue + dotnet restore --verbosity Error | Should BeNullOrEmpty + dotnet build | ?{ $_ -match "Compiling SendGreeting for .NETStandard,Version=v1.5" } | Should Not BeNullOrEmpty + } + + It "Should be able to use the module" { + Import-Module -ErrorAction Stop ./bin/Debug/netstandard1.5/SendGreeting.dll + Send-Greeting -Name World | Should Be "Hello World!" + Remove-Module SendGreeting + } +} diff --git a/docs/cmdlet-example/SendGreeting.cs b/docs/cmdlet-example/SendGreeting.cs new file mode 100644 index 0000000000..cd6055027c --- /dev/null +++ b/docs/cmdlet-example/SendGreeting.cs @@ -0,0 +1,30 @@ +// Example from https://msdn.microsoft.com/en-us/library/dd901838(v=vs.85).aspx + +using System.Management.Automation; // Windows PowerShell assembly. + +namespace SendGreeting +{ + // Declare the class as a cmdlet and specify and + // appropriate verb and noun for the cmdlet name. + [Cmdlet(VerbsCommunications.Send, "Greeting")] + public class SendGreetingCommand : Cmdlet + { + // Declare the parameters for the cmdlet. + [Parameter(Mandatory=true)] + public string Name + { + get { return name; } + set { name = value; } + } + private string name; + + // Overide the ProcessRecord method to process + // the supplied user name and write out a + // greeting to the user by calling the WriteObject + // method. + protected override void ProcessRecord() + { + WriteObject("Hello " + name + "!"); + } + } +} diff --git a/docs/cmdlet-example/project.json b/docs/cmdlet-example/project.json new file mode 100644 index 0000000000..e68b325575 --- /dev/null +++ b/docs/cmdlet-example/project.json @@ -0,0 +1,19 @@ +{ + "name": "SendGreeting", + "description": "Example C# Cmdlet project", + "version": "1.0.0-*", + + "dependencies": { + "Microsoft.PowerShell.5.ReferenceAssemblies": "1.0.0-*" + }, + + "frameworks": { + "netstandard1.5": { + "imports": [ "net40" ], + "dependencies": { + "Microsoft.NETCore": "5.0.1-*", + "Microsoft.NETCore.Portable.Compatibility": "1.0.1-*" + } + } + } +} diff --git a/test/powershell/CmdletExample.Tests.ps1 b/test/powershell/CmdletExample.Tests.ps1 new file mode 100644 index 0000000000..449d850ee2 --- /dev/null +++ b/test/powershell/CmdletExample.Tests.ps1 @@ -0,0 +1,6 @@ +try { + Push-Location $PSScriptRoot/../../docs/cmdlet-example + ./SendGreeting.Tests.ps1 +} finally { + Pop-Location +}