Adding basic packing script(s)
This commit is contained in:
Родитель
a470f5a380
Коммит
8fa9b5a5c4
|
@ -254,3 +254,4 @@ paket-files/
|
|||
*.*~
|
||||
*.swp
|
||||
backup/
|
||||
nupkgs/
|
||||
|
|
|
@ -5,10 +5,14 @@ VisualStudioVersion = 15.0.25902.2
|
|||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-packer", "dotnet-packer\dotnet-packer.csproj", "{0A4D9107-C593-4AFB-AB17-559324D2ECB4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.CommandLine", "System.CommandLine\System.CommandLine.csproj", "{BAD7C7D4-FB52-4FF3-9037-5BA935861E51}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleTargets.PackerTarget", "SampleTargets.PackerTarget\SampleTargets.PackerTarget.csproj", "{463C66F0-921D-4D34-8BDE-7C9D0BFFAF7B}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B798940E-346F-4068-A067-A7D3AEA708DB}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
pack.ps1 = pack.ps1
|
||||
pack.sh = pack.sh
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -19,14 +23,10 @@ Global
|
|||
{0A4D9107-C593-4AFB-AB17-559324D2ECB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0A4D9107-C593-4AFB-AB17-559324D2ECB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0A4D9107-C593-4AFB-AB17-559324D2ECB4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BAD7C7D4-FB52-4FF3-9037-5BA935861E51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BAD7C7D4-FB52-4FF3-9037-5BA935861E51}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BAD7C7D4-FB52-4FF3-9037-5BA935861E51}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BAD7C7D4-FB52-4FF3-9037-5BA935861E51}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{463C66F0-921D-4D34-8BDE-7C9D0BFFAF7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{463C66F0-921D-4D34-8BDE-7C9D0BFFAF7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{463C66F0-921D-4D34-8BDE-7C9D0BFFAF7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{463C66F0-921D-4D34-8BDE-7C9D0BFFAF7B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{463C66F0-921D-4D34-8BDE-7C9D0BFFAF7B}.Release|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{463C66F0-921D-4D34-8BDE-7C9D0BFFAF7B}.Release|Any CPU.Build.0 = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
37
README.md
37
README.md
|
@ -1,12 +1,43 @@
|
|||
# Extensibility of the CLI toolset hackathon sample
|
||||
# Extensibility of the CLI toolset - sample for the hackathon
|
||||
|
||||
This repository contains the basic sample of an extension to the MSBuild edition of the [CLI toolset for .NET Core](https://github.com/dotnet/cli). The idea of the repo is to provide an easy way to get started with developing extensions to the CLI and the SDK in terms of both MSBuild targets as well as "classic" tools that can be invoked using the `dotnet-<command>` invocation pattern.
|
||||
|
||||
The application in question is a proof-of-concept tool that allows the user to package a published application as a zip file. It is implemented as:
|
||||
The extension in question is a proof-of-concept tool that allows the user to package a published application as a zip file. It is implemented as:
|
||||
|
||||
1. A target that depends on the publish target from the SDK
|
||||
2. A tool that allows the user to invoke the target as `dotnet packer`
|
||||
|
||||
The `sample` directory contains a sample csproj file with added artifacts that can be restored using `dotnet restore` and that will allow this tool to run.
|
||||
## Testing it out
|
||||
|
||||
While in the solution folder, run the following:
|
||||
|
||||
```bash
|
||||
./pack.sh
|
||||
cd sample/ConsumingProject
|
||||
dotnet restore
|
||||
dotnet packer
|
||||
```
|
||||
|
||||
> **Note:** if you are using Windows, use `pack.ps1` script instead of the \*.sh one.
|
||||
|
||||
## SampleTargets.PackerTarget
|
||||
This folder contains the target implementation. It also specifies configuration within the project file to package the needed files into a nuget package. Note the `build` folder in there. It contains the targets that will extend the project file. You can read more about this approach on [NuGet documentation](https://docs.nuget.org/ndocs/create-packages/creating-a-package#including-msbuild-props-and-targets-in-a-package).
|
||||
|
||||
You can create a nupkg out of this project by simply using the `dotnet pack` command.
|
||||
|
||||
## dotnet-packer
|
||||
This is a very simple console application that allows the user to invoke MSBuild without having to learn MSBuild invocation syntax. It also demonstrates an approach to building CLI tools that can interact with the targets extensions.
|
||||
|
||||
You can create a nupkg out of this project by simply using the `dotnet pack` command.
|
||||
|
||||
## A short FAQ
|
||||
|
||||
### Is the tool neccessary if I just want to create a target?
|
||||
No, it is not. You can have a target project just include the target and invoke it using `dotnet msbuild /t:<target-name>`. The tool is just a nicer user experience.
|
||||
|
||||
### When should I use which one?
|
||||
Targets should be used if your extension depends on something from the build process (e.g. output) or if it needs/wants to extend something in the build process (e.g. run after a given step, depend on a given step).
|
||||
|
||||
A tool should be used in those situations where there is no need to interact with the build process.
|
||||
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace CliCommands.Packer.Task
|
|||
Log.LogMessage(MessageImportance.Normal, $"The package will be published as {metadata.PackageFileName}");
|
||||
try {
|
||||
packer.Pack(metadata);
|
||||
Log.LogMessage(MessageImportance.High, "Packaging complete!");
|
||||
Log.LogMessage(MessageImportance.High, $"Packaging complete! The package can be found in {metadata.PackageFileName}.");
|
||||
return true;
|
||||
} catch (Exception ex)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
|
||||
<PropertyGroup>
|
||||
<Description>Sample Packer</Description>
|
||||
|
@ -15,6 +15,12 @@
|
|||
<ItemGroup>
|
||||
<None Include="build\SampleTargets.PackerTarget.targets" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="build\*.targets;$(OutputPath)\*.dll;$(OutputPath)\*.json">
|
||||
<Pack>true</Pack>
|
||||
<PackagePath>build\</PackagePath>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Sdk">
|
||||
<Version>1.0.0-alpha-20161029-1</Version>
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>SampleTargets.PackerTarget</id>
|
||||
<version>0.1.0-preview</version>
|
||||
<authors>Zlatko Knezevic</authors>
|
||||
<description>A sample extension to the MSBuild-based CLI toolset</description>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="build\**" target="build" />
|
||||
<file src="bin\Debug\netstandard1.3\*.dll" target="build" />
|
||||
<file src="bin\Debug\netstandard1.3\*.json" target="build" />
|
||||
</files>
|
||||
</package>
|
|
@ -1,3 +0,0 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("System.CommandLine.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100039ac461fa5c82c7dd2557400c4fd4e9dcdf7ac47e3d572548c04cd4673e004916610f4ea5cbf86f2b1ca1cb824f2a7b3976afecfcf4eb72d9a899aa6786effa10c30399e6580ed848231fec48374e41b3acf8811931343fc2f73acf72dae745adbcb7063cc4b50550618383202875223fc75401351cd89c44bf9b50e7fa3796")]
|
|
@ -1,488 +0,0 @@
|
|||
# System.CommandLine
|
||||
|
||||
The purpose of this library is to make command line tools first class by
|
||||
providing a command line parser. We've already made an attempt in 2009 but
|
||||
that wasn't a design we (or the community) was
|
||||
[happy with](http://tirania.org/blog/archive/2009/Feb-21.html).
|
||||
|
||||
Here are the goals:
|
||||
|
||||
* Designed for cross-platform usage
|
||||
* Lightweight with minimal configuration
|
||||
* Optional but built-in support for help, validation, and response files
|
||||
* Support for multiple commands, like version control tools
|
||||
|
||||
[Syntax](#syntax) and [API samples](#api-samples) are below.
|
||||
|
||||
## Why a new library?
|
||||
|
||||
There is already a set of libraries available for command line parsing, such as:
|
||||
|
||||
* [Mono.Options][Mono.Options] (also known as `NDesk.Options`)
|
||||
* [CommandLine][CommandLine]
|
||||
* An [internal one][vance] that Vance Morrison wrote many years ago.
|
||||
|
||||
[Mono.Options]: http://tirania.org/blog/archive/2008/Oct-14.html
|
||||
[CommandLine]: https://github.com/gsscoder/commandline
|
||||
[vance]: https://github.com/dotnet/buildtools/blob/master/src/common/CommandLine.cs
|
||||
|
||||
So the question is: why a new one? There are a couple of reasons:
|
||||
|
||||
1. We want to support a syntax that feels natural when used across platforms. In
|
||||
particular, we want to be very close to the Unix- and GNU style.
|
||||
|
||||
2. We need something that is quite low level. In particular we don't want to
|
||||
have a library that requires reflection for attribute discovery or for
|
||||
setting properties.
|
||||
|
||||
3. We want an experience that achieves an extremely minimal setup in terms of
|
||||
lines of code required for parsing.
|
||||
|
||||
While some of the libraries solve some of these aspects none of them solve the
|
||||
combination.
|
||||
|
||||
Of course, providing a command line parser isn't just providing a parsing
|
||||
mechanism: in order to be usable, the library has to be opinionated in both the
|
||||
supported syntax as well as in the shape of the APIs. In the BCL, we've always
|
||||
taken the stance that we want to provide a layered experience that allows
|
||||
getting the 80% scenario done, while allowing to be extensible for a potential
|
||||
long tail of additional scenarios. If that means we've to make policy decisions
|
||||
so be it because not making those forces all of our consumers to come up with
|
||||
their own policy.
|
||||
|
||||
That being said, the goal isn't to provide the final command line parser
|
||||
library. In fact, I'm not aware of any platform that gets away with having a
|
||||
single one. If you're happy with the one you're already using or if you even
|
||||
wrote your own: that's perfectly fine. But after one and a half decades it's
|
||||
time for the BCL to provide a built-in experience as well.
|
||||
|
||||
## Work in progress
|
||||
|
||||
* Should we support a case insensitive mode?
|
||||
* Should we have a way to name option arguments, e.g. `DefineOption("n=name")`?
|
||||
* Should provide a string based approach to define usage?
|
||||
* Should we provide an error handler?
|
||||
* Should we provide a help request handler?
|
||||
* Should we expose the response file hander?
|
||||
* Should we allow "empty" commands, so that the tool can support options without
|
||||
a command, like `git --version`?
|
||||
|
||||
## Syntax
|
||||
|
||||
The syntax conventions are heavily inspired by the following existing
|
||||
conventions:
|
||||
|
||||
* [Unix History][Unix-History]
|
||||
* [POSIX Conventions][POSIX-Conventions]
|
||||
* [GNU Standards for Command Line Interfaces][GNU]
|
||||
* [GNU List of standard option names][GNU-Options]
|
||||
|
||||
[Unix-History]: http://catb.org/~esr/writings/taoup/html/ch10s05.html
|
||||
[POSIX-Conventions]: http://www.cs.unicam.it/piergallini/home/materiale/gc/java/essential/attributes/_posix.html
|
||||
[GNU]: http://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html
|
||||
[GNU-Options]: http://www.gnu.org/prep/standards/html_node/Option-Table.html#Option-Table
|
||||
|
||||
In general, all strings are treated in a case sensitive way. This allows
|
||||
supporting options that only differ by case, which is pretty common on
|
||||
Unix systems, e.g.
|
||||
|
||||
# This reverses the output
|
||||
$ ls -r *.txt
|
||||
# This does a recursive search
|
||||
$ ls -R *.txt
|
||||
|
||||
### Single character options
|
||||
|
||||
Single character options are delimited by a single dash, e.g.
|
||||
|
||||
$ tool -x -d -f
|
||||
|
||||
They can be *bundled* together, such as
|
||||
|
||||
$ tool -xdf
|
||||
|
||||
Please note that slashes aren't supported.
|
||||
|
||||
### Keyword options
|
||||
|
||||
Keyword options are delimited by two dashes, such as:
|
||||
|
||||
$ tool --verbose
|
||||
|
||||
### Option arguments
|
||||
|
||||
Both, the single letter form, as well as the long forms, support arguments.
|
||||
Arguments must be separated by either a space, an equal sign or a colon:
|
||||
|
||||
# All three forms are identical:
|
||||
$ tool --out result.exe
|
||||
$ tool --out=result.exe
|
||||
$ tool --out:result.exe
|
||||
|
||||
Multiple spaces are allowed as well:
|
||||
|
||||
$ tool --out result.exe
|
||||
$ tool --out = result.exe
|
||||
$ tool --out : result.exe
|
||||
|
||||
This even works when combined with bundling, but in that case only the last
|
||||
option can have an argument. So this:
|
||||
|
||||
$ tool -am "hello"
|
||||
|
||||
is equivalent to:
|
||||
|
||||
$ tool -a -m "hello"
|
||||
|
||||
### Multiple occurrences
|
||||
|
||||
Unix has a strong tradition for scripting. In order to make it easier to forward
|
||||
arguments to scripts, it's common practice to allow options to appear more than
|
||||
once. The semantics are that the last one wins. So this:
|
||||
|
||||
$ tool -a this -b -a that
|
||||
|
||||
has the same effect as that:
|
||||
|
||||
$ tool -b -a that
|
||||
|
||||
### Parameters
|
||||
|
||||
Parameters, sometimes also called non-option arguments, can be anywhere in the
|
||||
input:
|
||||
|
||||
# Both forms equivalent:
|
||||
$ tool input1.ext input2.ext -o result.ext
|
||||
$ tool input1.ext -o result.ext input2.ext
|
||||
|
||||
### Commands
|
||||
|
||||
Very often, tools have multiple commands with independent options. Good example
|
||||
are version control tools, e.g.
|
||||
|
||||
$ tool fetch origin --prune
|
||||
$ tool commit -m 'Message'
|
||||
|
||||
### Response files
|
||||
|
||||
It's common practice to allow passing command line arguments via response files.
|
||||
This can look as follows:
|
||||
|
||||
$ tool -f -r @D:\src\defaults.rsp --additional
|
||||
|
||||
The API supports multiple response files being passed in. It will simply expand
|
||||
those in-place, i.e. it's valid to have additional options and parameters
|
||||
before, as well as after the response file reference.
|
||||
|
||||
## API Samples
|
||||
|
||||
### Hello world
|
||||
|
||||
```C#
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
|
||||
static class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var addressee = "world";
|
||||
|
||||
ArgumentSyntax.Parse(args, syntax =>
|
||||
{
|
||||
syntax.DefineOption("n|name", ref addressee, "The addressee to greet");
|
||||
});
|
||||
|
||||
Console.WriteLine("Hello {0}!", addressee);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Usage looks as follows:
|
||||
|
||||
```
|
||||
$ ./hello -h
|
||||
usage: hello [-n <arg>]
|
||||
|
||||
-n, --name The addressee to greet
|
||||
|
||||
$ ./hello
|
||||
Hello world!
|
||||
$ ./hello -n Jennifer
|
||||
Hello Jennifer!
|
||||
$ ./hello --name Tom
|
||||
Hello Tom!
|
||||
$ ./hello -x
|
||||
error: invalid option -x
|
||||
```
|
||||
|
||||
### Defining options
|
||||
|
||||
The `ArgumentSyntax` class allows defining options and parameters for any data
|
||||
type. In order to parse the value, you need to supply a `Func<string, T>` that
|
||||
performs the parsing. So if you want to use a guid, you could define an option
|
||||
like this:
|
||||
|
||||
```C#
|
||||
Guid guid = Guid.Empty;
|
||||
syntax.DefineOption("g|guid", ref guid, Guid.Parse, "The GUID to use");
|
||||
```
|
||||
|
||||
The library provides overloads that handle the most common types, such as
|
||||
`string`, `int`, and `bool`, so that you don't have to pass in parsers for
|
||||
those.
|
||||
|
||||
Boolean options are specially handled in that they are considered flags, i.e.
|
||||
they don't require an argument -- they are simply considered `true` if they are
|
||||
specified. However you can still explicitly pass in `true` or `false`. So this
|
||||
|
||||
$ tool -x
|
||||
|
||||
Is equivalent to
|
||||
|
||||
$ tool -x:true
|
||||
|
||||
The syntax used to define the option supports multiple names by separating them
|
||||
using a pipe. All names are aliases for the same option. For diagnostic
|
||||
purposes, the first name will be used. By convention that should be the short
|
||||
name, but it's really up to you.
|
||||
|
||||
### Defining parameters
|
||||
|
||||
Parameters work in a very similar way:
|
||||
|
||||
```C#
|
||||
Guid guid = Guid.Empty;
|
||||
syntax.DefineParameter("guid", ref guid, Guid.Parse, "The GUID to use");
|
||||
```
|
||||
|
||||
However, since parameters are matched by position and not by using a named
|
||||
option, the name is only used for diagnostic purposes and to render a readable
|
||||
syntax. Hence, they don't support the pipe syntax because having multiple names
|
||||
wouldn't make any sense there.
|
||||
|
||||
Please note that parameters must be specified after options. The reason being
|
||||
that the parser needs to know all options before it can match parameters.
|
||||
Otherwise parsing this command would be ambiguous:
|
||||
|
||||
$ tool -x one two
|
||||
|
||||
Without knowing whether `-x` takes an argument, it's not clear whether `one`
|
||||
will be an argument or the first parameter.
|
||||
|
||||
### Defining option and parameter lists
|
||||
|
||||
Both, options and parameters, support the notion of lists. For example, consider
|
||||
a compiler `comp`:
|
||||
|
||||
$ comp -D DEBUG -D ARCH=x86 source1 source2 -out:hello
|
||||
|
||||
You would define the options and parameters as follows:
|
||||
|
||||
```C#
|
||||
string output = string.Empty;
|
||||
IReadOnlyList<string> defines = Array.Empty<string>();
|
||||
IReadOnlyList<string> sources = Array.Empty<string>();
|
||||
|
||||
syntax.DefineOption("out", ref output, "Output name");
|
||||
syntax.DefineOptionList("D|define", ref defines, "Preprocessor definitions");
|
||||
syntax.DefineParameterList("source", ref sources, "The source files to compile");
|
||||
```
|
||||
|
||||
In general, you can define multiple option lists but only one parameter list.
|
||||
The reason being that a parameter list will consume all remaining parameters.
|
||||
You can define individual parameters and a parameter list but the parameter list
|
||||
must be after the individual parameters otherwise the individual ones will never
|
||||
be matched.
|
||||
|
||||
### Defining commands
|
||||
|
||||
Commands are defined in a similar way to options and parameters. The way they
|
||||
are associated with options and commands is by order:
|
||||
|
||||
```C#
|
||||
var command = string.Empty;
|
||||
var prune = false;
|
||||
var message = string.Empty;
|
||||
var amend = false;
|
||||
|
||||
syntax.DefineCommand("pull", ref command, "Pull from another repo");
|
||||
syntax.DefineOption("p|prune", ref prune, "Prune branches");
|
||||
|
||||
syntax.DefineCommand("commit", ref command, "Committing changes");
|
||||
syntax.DefineOption("m|message", ref message, "The message to use");
|
||||
syntax.DefineOption("amend", ref amend, "Amend existing commit");
|
||||
```
|
||||
|
||||
In this case the `pull` command has a `-p` option and the `commit` command has
|
||||
`-m` and `--amend` options. It's worth noting that you can use the same option
|
||||
name between different commands as they are logically in different scopes.
|
||||
|
||||
In order to check which command was used you've two options. You can either
|
||||
use the version we used above in which case the `ref` variable passed to
|
||||
`DefineCommand` will contain the name of the command that was specified. But
|
||||
you're not limited to just plain strings. For example, this will work as well:
|
||||
|
||||
```C#
|
||||
enum Command { Pull, Commit }
|
||||
|
||||
// ...
|
||||
|
||||
Command command = Command.Pull;
|
||||
syntax.DefineCommand("pull", ref command, Command.Pull, "Pull from another repo");
|
||||
syntax.DefineCommand("commit", ref command, Command.Commit, "Committing changes");
|
||||
```
|
||||
|
||||
### Custom help
|
||||
|
||||
By default, `ArgumentSyntax` will display the help and exit when `-?`, `-h` or
|
||||
`--help` is specified. Some tools perform different actions, for instance `git`,
|
||||
which displays the help on the command line when `-h` is used but opens the web
|
||||
browser when `--help` is used.
|
||||
|
||||
You can support this by handling help yourself:
|
||||
|
||||
```C#
|
||||
ArgumentSyntax.Parse(args, syntax =>
|
||||
{
|
||||
// Turn off built-in help processing
|
||||
|
||||
syntax.HandleHelp = false;
|
||||
|
||||
// Define your own options
|
||||
|
||||
syntax.DefineOption("n|name", ref addressee, "The addressee to greet");
|
||||
|
||||
// Define custom help options. Optionally, you can hide those options
|
||||
// from the help text.
|
||||
|
||||
var longHelp = syntax.DefineOption("help", false);
|
||||
longHelp.IsHidden = true;
|
||||
|
||||
var quickHelp = syntax.DefineOption("h", false);
|
||||
quickHelp.IsHidden = true;
|
||||
|
||||
if (longHelp.Value)
|
||||
{
|
||||
// Open a browser
|
||||
var url = "https://en.wikipedia.org/wiki/%22Hello,_World!%22_program";
|
||||
Process.Start(url);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
else if (quickHelp.Value)
|
||||
{
|
||||
// Show help text. Even if you disable built-in help processing
|
||||
// you can still use the built-in help page generator. Optionally,
|
||||
// you can ask it to word wrap it according to some maximum, such
|
||||
// as the width of the console window.
|
||||
var maxWidth = Console.WindowWidth - 2;
|
||||
var helpText = syntax.GetHelpText(maxWidth);
|
||||
Console.WriteLine(helpText);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Additional validation
|
||||
|
||||
Let's say you want to perform additional validation, such as that supplied
|
||||
arguments point to valid files or that certain options aren't used in
|
||||
combination. You can do this by simply adding a bit of validation code at
|
||||
the end of the `Parse` method that calls `ReportError`.
|
||||
|
||||
```C#
|
||||
ArgumentSyntax.Parse(args, syntax =>
|
||||
{
|
||||
syntax.DefineOption("n|name", ref addressee, "The addressee to greet");
|
||||
|
||||
if (addressee.Any(char.IsWhiteSpace))
|
||||
syntax.ReportError("addressee cannot contain whitespace");
|
||||
});
|
||||
```
|
||||
|
||||
Usage will look like this:
|
||||
|
||||
```
|
||||
$ ./hello -n "Immo Landwerth"
|
||||
error: addressee cannot contain whitespace
|
||||
```
|
||||
|
||||
### Custom error handling
|
||||
|
||||
There are cases where you want to use `ArgumentSyntax` in such a way that user
|
||||
errors shouldn't result in the process being terminated. You can do this by
|
||||
disabling the built-in error handling. In that case, the `ReportError` method --
|
||||
and thus the `Parse` method -- will throw `ArgumentSyntaxException`.
|
||||
|
||||
```C#
|
||||
try
|
||||
{
|
||||
ArgumentSyntax.Parse(args, syntax =>
|
||||
{
|
||||
syntax.HandleErrors = false;
|
||||
syntax.DefineOption("n|name", ref addressee);
|
||||
});
|
||||
}
|
||||
catch (ArgumentSyntaxException ex)
|
||||
{
|
||||
Console.WriteLine("Ooops... something didn't go well.");
|
||||
Console.WriteLine(ex.Message);
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
### Accessing options and parameters
|
||||
|
||||
The `ArgumentSyntax` object also provides access to the defined options and
|
||||
parameters. The `Parse` method returns the used instance, so you can use that
|
||||
to access them. You can either access all of the defined ones or you can access
|
||||
only the ones that are relevant to the currently active command.
|
||||
|
||||
```C#
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var addressee = "world";
|
||||
|
||||
var result = ArgumentSyntax.Parse(args, syntax =>
|
||||
{
|
||||
syntax.DefineOption("n|name", ref addressee, "The addressee to greet");
|
||||
|
||||
if (addressee.Any(char.IsWhiteSpace))
|
||||
syntax.ReportError("adressee cannot contain whitespace");
|
||||
});
|
||||
|
||||
foreach (var argument in result.GetActiveArguments())
|
||||
{
|
||||
if (argument.IsOption)
|
||||
{
|
||||
var names = string.Join(", ", argument.GetDisplayNames());
|
||||
Console.WriteLine("Option {0}", names);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Parameter {0}", argument.GetDisplayName());
|
||||
}
|
||||
|
||||
Console.WriteLine("Help : {0}", argument.Help);
|
||||
Console.WriteLine("IsHidden : {0}", argument.IsHidden);
|
||||
Console.WriteLine("Value : {0}", argument.Value);
|
||||
Console.WriteLine("DefaultValue : {0}", argument.DefaultValue);
|
||||
Console.WriteLine("IsSpecified : {0}", argument.IsSpecified);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Turning off response files
|
||||
|
||||
In case you don't want consumers to use response files or you need to process
|
||||
parameters that could be prefixed with an `@`-sign, you can disable response
|
||||
file expansion:
|
||||
|
||||
```C#
|
||||
ArgumentSyntax.Parse(args, syntax =>
|
||||
{
|
||||
syntax.HandleResponseFiles = false;
|
||||
|
||||
// ...
|
||||
});
|
||||
```
|
|
@ -1,50 +0,0 @@
|
|||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
|
||||
<PropertyGroup>
|
||||
<Description>CLI commandline command parsing</Description>
|
||||
<Copyright>Microsoft Corporation, All rights reserved</Copyright>
|
||||
<VersionPrefix>0.1.0</VersionPrefix>
|
||||
<TargetFramework>netstandard1.6</TargetFramework>
|
||||
<AssemblyName>System.CommandLine</AssemblyName>
|
||||
<AssemblyOriginatorKeyFile>../tools/Key.snk</AssemblyOriginatorKeyFile>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||
<PackageTags>Command line parsing support corefxlab</PackageTags>
|
||||
<PackageReleaseNotes>Pre-release package, for testing only</PackageReleaseNotes>
|
||||
<PackageIconUrl>http://go.microsoft.com/fwlink/?LinkID=288859</PackageIconUrl>
|
||||
<PackageProjectUrl>https://github.com/dotnet/corefxlab</PackageProjectUrl>
|
||||
<PackageLicenseUrl>http://go.microsoft.com/fwlink/?LinkId=329770</PackageLicenseUrl>
|
||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="**\*.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
|
||||
<EmbeddedResource Include="**\*.resx" Exclude="bin\**;obj\**;**\*.xproj;packages\**">
|
||||
<Link>System.Strings</Link>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="compiler\resources\**\*" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Sdk">
|
||||
<Version>1.0.0-alpha-20161102-2</Version>
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NETStandard.Library">
|
||||
<Version>1.6</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.6' ">
|
||||
<PackageReference Include="System.Runtime.Extensions">
|
||||
<Version>4.1.0</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>bad7c7d4-fb52-4ff3-9037-5ba935861e51</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.6' ">
|
||||
<DefineConstants>$(DefineConstants);NETSTANDARD1_6</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -1,107 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace System.CommandLine
|
||||
{
|
||||
public abstract class Argument
|
||||
{
|
||||
internal Argument(ArgumentCommand command, IEnumerable<string> names, bool isOption)
|
||||
{
|
||||
var nameArray = names.ToArray();
|
||||
Command = command;
|
||||
Name = nameArray.First();
|
||||
Names = new ReadOnlyCollection<string>(nameArray);
|
||||
IsOption = isOption;
|
||||
}
|
||||
|
||||
public ArgumentCommand Command { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public ReadOnlyCollection<string> Names { get; private set; }
|
||||
|
||||
public string Help { get; set; }
|
||||
|
||||
public bool IsOption { get; private set; }
|
||||
|
||||
public bool IsParameter
|
||||
{
|
||||
get { return !IsOption; }
|
||||
}
|
||||
|
||||
public bool IsSpecified { get; private set; }
|
||||
|
||||
public bool IsHidden { get; set; }
|
||||
|
||||
public virtual bool IsList
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public object Value
|
||||
{
|
||||
get { return GetValue(); }
|
||||
}
|
||||
|
||||
public object DefaultValue
|
||||
{
|
||||
get { return GetDefaultValue(); }
|
||||
}
|
||||
|
||||
public bool IsActive
|
||||
{
|
||||
get { return Command == null || Command.IsActive; }
|
||||
}
|
||||
|
||||
public abstract bool IsFlag { get; }
|
||||
|
||||
internal abstract object GetValue();
|
||||
|
||||
internal abstract object GetDefaultValue();
|
||||
|
||||
internal void MarkSpecified()
|
||||
{
|
||||
IsSpecified = true;
|
||||
}
|
||||
|
||||
public string GetDisplayName()
|
||||
{
|
||||
return GetDisplayName(Name);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetDisplayNames()
|
||||
{
|
||||
return Names.Select(GetDisplayName);
|
||||
}
|
||||
|
||||
private string GetDisplayName(string name)
|
||||
{
|
||||
return IsOption ? GetOptionDisplayName(name) : GetParameterDisplayName(name);
|
||||
}
|
||||
|
||||
private static string GetOptionDisplayName(string name)
|
||||
{
|
||||
var modifier = name.Length == 1 ? @"-" : @"--";
|
||||
return modifier + name;
|
||||
}
|
||||
|
||||
private static string GetParameterDisplayName(string name)
|
||||
{
|
||||
return @"<" + name + @">";
|
||||
}
|
||||
|
||||
public virtual string GetDisplayValue()
|
||||
{
|
||||
return Value == null ? string.Empty : Value.ToString();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return GetDisplayName();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace System.CommandLine
|
||||
{
|
||||
public abstract class ArgumentCommand
|
||||
{
|
||||
internal ArgumentCommand(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public string Help { get; set; }
|
||||
|
||||
public object Value
|
||||
{
|
||||
get { return GetValue(); }
|
||||
}
|
||||
|
||||
public bool IsHidden { get; set; }
|
||||
|
||||
public bool IsActive { get; private set; }
|
||||
|
||||
internal abstract object GetValue();
|
||||
|
||||
internal void MarkActive()
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace System.CommandLine
|
||||
{
|
||||
public sealed class ArgumentCommand<T> : ArgumentCommand
|
||||
{
|
||||
internal ArgumentCommand(string name, T value)
|
||||
: base(name)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public new T Value { get; private set; }
|
||||
|
||||
internal override object GetValue()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace System.CommandLine
|
||||
{
|
||||
internal static class ArgumentLexer
|
||||
{
|
||||
public static IReadOnlyList<ArgumentToken> Lex(IEnumerable<string> args, Func<string, IEnumerable<string>> responseFileReader = null)
|
||||
{
|
||||
var result = new List<ArgumentToken>();
|
||||
|
||||
// We'll split the arguments into tokens.
|
||||
//
|
||||
// A token combines the modifier (/, -, --), the option name, and the option
|
||||
// value.
|
||||
//
|
||||
// Please note that this code doesn't combine arguments. It only provides
|
||||
// some pre-processing over the arguments to split out the modifier,
|
||||
// option, and value:
|
||||
//
|
||||
// { "--out", "out.exe" } ==> { new ArgumentToken("--", "out", null),
|
||||
// new ArgumentToken(null, null, "out.exe") }
|
||||
//
|
||||
// {"--out:out.exe" } ==> { new ArgumentToken("--", "out", "out.exe") }
|
||||
//
|
||||
// The reason it doesn't combine arguments is because it depends on the actual
|
||||
// definition. For example, if --out is a flag (meaning it's of type bool) then
|
||||
// out.exe in the first example wouldn't be considered its value.
|
||||
//
|
||||
// The code also handles the special -- token which indicates that the following
|
||||
// arguments shouldn't be considered options.
|
||||
//
|
||||
// Finally, this code will also expand any reponse file entries, assuming the caller
|
||||
// gave us a non-null reader.
|
||||
|
||||
var hasSeenDashDash = false;
|
||||
|
||||
foreach (var arg in ExpandReponseFiles(args, responseFileReader))
|
||||
{
|
||||
// If we've seen a -- already, then we'll treat one as a plain name, that is
|
||||
// without a modifier or value.
|
||||
|
||||
if (!hasSeenDashDash && arg == @"--")
|
||||
{
|
||||
hasSeenDashDash = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
string modifier;
|
||||
string name;
|
||||
string value;
|
||||
|
||||
if (hasSeenDashDash)
|
||||
{
|
||||
modifier = null;
|
||||
name = arg;
|
||||
value = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we haven't seen the -- separator, we're looking for options.
|
||||
// Options have leading modifiers, i.e. /, -, or --.
|
||||
//
|
||||
// Options can also have values, such as:
|
||||
//
|
||||
// -f:false
|
||||
// --name=hello
|
||||
|
||||
string nameAndValue;
|
||||
|
||||
if (!TryExtractOption(arg, out modifier, out nameAndValue))
|
||||
{
|
||||
name = arg;
|
||||
value = null;
|
||||
}
|
||||
else if (!TrySplitNameValue(nameAndValue, out name, out value))
|
||||
{
|
||||
name = nameAndValue;
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
|
||||
var token = new ArgumentToken(modifier, name, value);
|
||||
result.Add(token);
|
||||
}
|
||||
|
||||
// Single letter options can be combined, for example the following two
|
||||
// forms are considered equivalent:
|
||||
//
|
||||
// (1) -xdf
|
||||
// (2) -x -d -f
|
||||
//
|
||||
// In order to free later phases from handling this case, we simply expand
|
||||
// single letter options to the second form.
|
||||
|
||||
for (var i = result.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (IsOptionBundle(result[i]))
|
||||
ExpandOptionBundle(result, i);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static IEnumerable<string> ExpandReponseFiles(IEnumerable<string> args, Func<string, IEnumerable<string>> responseFileReader)
|
||||
{
|
||||
foreach (var arg in args)
|
||||
{
|
||||
if (responseFileReader == null || !arg.StartsWith(@"@"))
|
||||
{
|
||||
yield return arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
var fileName = arg.Substring(1);
|
||||
|
||||
var responseFileArguments = responseFileReader(fileName);
|
||||
|
||||
// The reader can suppress expanding this response file by
|
||||
// returning null. In that case, we'll treat the response
|
||||
// file token as a regular argument.
|
||||
|
||||
if (responseFileArguments == null)
|
||||
{
|
||||
yield return arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var responseFileArgument in responseFileArguments)
|
||||
yield return responseFileArgument.Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsOptionBundle(ArgumentToken token)
|
||||
{
|
||||
return token.IsOption &&
|
||||
token.Modifier == @"-" &&
|
||||
token.Name.Length > 1;
|
||||
}
|
||||
|
||||
private static void ExpandOptionBundle(IList<ArgumentToken> receiver, int index)
|
||||
{
|
||||
var options = receiver[index].Name;
|
||||
receiver.RemoveAt(index);
|
||||
|
||||
foreach (var c in options)
|
||||
{
|
||||
var name = char.ToString(c);
|
||||
var expandedToken = new ArgumentToken(@"-", name, null);
|
||||
receiver.Insert(index, expandedToken);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryExtractOption(string text, out string modifier, out string remainder)
|
||||
{
|
||||
return TryExtractOption(text, @"--", out modifier, out remainder) ||
|
||||
TryExtractOption(text, @"-", out modifier, out remainder);
|
||||
}
|
||||
|
||||
private static bool TryExtractOption(string text, string prefix, out string modifier, out string remainder)
|
||||
{
|
||||
if (text.StartsWith(prefix))
|
||||
{
|
||||
remainder = text.Substring(prefix.Length);
|
||||
modifier = prefix;
|
||||
return true;
|
||||
}
|
||||
|
||||
remainder = null;
|
||||
modifier = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TrySplitNameValue(string text, out string name, out string value)
|
||||
{
|
||||
return TrySplitNameValue(text, ':', out name, out value) ||
|
||||
TrySplitNameValue(text, '=', out name, out value);
|
||||
}
|
||||
|
||||
private static bool TrySplitNameValue(string text, char separator, out string name, out string value)
|
||||
{
|
||||
var i = text.IndexOf(separator);
|
||||
if (i >= 0)
|
||||
{
|
||||
name = text.Substring(0, i);
|
||||
value = text.Substring(i + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
name = null;
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace System.CommandLine
|
||||
{
|
||||
public sealed class ArgumentList<T> : Argument
|
||||
{
|
||||
internal ArgumentList(ArgumentCommand command, IEnumerable<string> names, IReadOnlyList<T> defaultValue)
|
||||
: base(command, names, true)
|
||||
{
|
||||
Value = defaultValue;
|
||||
DefaultValue = defaultValue;
|
||||
}
|
||||
|
||||
internal ArgumentList(ArgumentCommand command, string name, IReadOnlyList<T> defaultValue)
|
||||
: base(command, new[] { name }, false)
|
||||
{
|
||||
Value = defaultValue;
|
||||
DefaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public override bool IsList
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public new IReadOnlyList<T> Value { get; private set; }
|
||||
|
||||
public new IReadOnlyList<T> DefaultValue { get; private set; }
|
||||
|
||||
public override bool IsFlag
|
||||
{
|
||||
get { return typeof(T) == typeof(bool); }
|
||||
}
|
||||
|
||||
internal override object GetValue()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
internal override object GetDefaultValue()
|
||||
{
|
||||
return DefaultValue;
|
||||
}
|
||||
|
||||
internal void SetValue(IReadOnlyList<T> value)
|
||||
{
|
||||
Value = value;
|
||||
MarkSpecified();
|
||||
}
|
||||
|
||||
public override string GetDisplayValue()
|
||||
{
|
||||
return string.Join(@", ", Value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,267 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace System.CommandLine
|
||||
{
|
||||
internal sealed class ArgumentParser
|
||||
{
|
||||
private readonly IReadOnlyList<ArgumentToken> _tokens;
|
||||
|
||||
public ArgumentParser(IEnumerable<string> arguments)
|
||||
: this(arguments, null)
|
||||
{
|
||||
}
|
||||
|
||||
public ArgumentParser(IEnumerable<string> arguments, Func<string, IEnumerable<string>> responseFileReader)
|
||||
{
|
||||
if (arguments == null)
|
||||
throw new ArgumentNullException("arguments");
|
||||
|
||||
_tokens = ArgumentLexer.Lex(arguments, responseFileReader);
|
||||
}
|
||||
|
||||
public bool TryParseCommand(string name)
|
||||
{
|
||||
var token = _tokens.FirstOrDefault();
|
||||
|
||||
if (token == null || token.IsOption || token.IsSeparator)
|
||||
return false;
|
||||
|
||||
if (!string.Equals(token.Name, name, StringComparison.Ordinal))
|
||||
return false;
|
||||
|
||||
token.MarkMatched();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryParseOption<T>(string diagnosticName, IReadOnlyCollection<string> names, Func<string, T> valueConverter, out T value)
|
||||
{
|
||||
IReadOnlyList<T> values;
|
||||
if (!TryParseOptionList(diagnosticName, names, valueConverter, out values))
|
||||
{
|
||||
value = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Please note that we don't verify that the option is only specified once.
|
||||
// It's tradition on Unix to allow single options to occur more than once,
|
||||
// with 'last one wins' semantics. This simplifies scripting because you
|
||||
// can easily combine arguments.
|
||||
|
||||
value = values.Last();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryParseOptionList<T>(string diagnosticName, IReadOnlyCollection<string> names, Func<string, T> valueConverter, out IReadOnlyList<T> values)
|
||||
{
|
||||
var result = new List<T>();
|
||||
var tokenIndex = 0;
|
||||
var isFlag = typeof(T) == typeof(bool);
|
||||
|
||||
while (tokenIndex < _tokens.Count)
|
||||
{
|
||||
if (TryParseOption(ref tokenIndex, names))
|
||||
{
|
||||
string valueText;
|
||||
if (TryParseOptionArgument(ref tokenIndex, isFlag, out valueText))
|
||||
{
|
||||
var value = ParseValue(diagnosticName, valueConverter, valueText);
|
||||
result.Add(value);
|
||||
}
|
||||
else if (isFlag)
|
||||
{
|
||||
var value = (T)(object)true;
|
||||
result.Add(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
var message = string.Format(Strings.OptionRequiresValueFmt, diagnosticName);
|
||||
throw new ArgumentSyntaxException(message);
|
||||
}
|
||||
}
|
||||
|
||||
tokenIndex++;
|
||||
}
|
||||
|
||||
if (!result.Any())
|
||||
{
|
||||
values = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
values = result.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryParseParameter<T>(string diagnosticName, Func<string, T> valueConverter, out T value)
|
||||
{
|
||||
foreach (var token in _tokens)
|
||||
{
|
||||
if (token.IsMatched || token.IsOption || token.IsSeparator)
|
||||
continue;
|
||||
|
||||
token.MarkMatched();
|
||||
|
||||
var valueText = token.Name;
|
||||
value = ParseValue(diagnosticName, valueConverter, valueText);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryParseParameterList<T>(string diagnosticName, Func<string, T> valueConverter, out IReadOnlyList<T> values)
|
||||
{
|
||||
var result = new List<T>();
|
||||
|
||||
T value;
|
||||
while (TryParseParameter(diagnosticName, valueConverter, out value))
|
||||
{
|
||||
result.Add(value);
|
||||
}
|
||||
|
||||
if (!result.Any())
|
||||
{
|
||||
values = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
values = result.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryParseOption(ref int tokenIndex, IReadOnlyCollection<string> names)
|
||||
{
|
||||
while (tokenIndex < _tokens.Count)
|
||||
{
|
||||
var a = _tokens[tokenIndex];
|
||||
|
||||
if (a.IsOption)
|
||||
{
|
||||
if (names.Any(n => string.Equals(a.Name, n, StringComparison.Ordinal)))
|
||||
{
|
||||
a.MarkMatched();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
tokenIndex++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryParseOptionArgument(ref int tokenIndex, bool isFlag, out string argument)
|
||||
{
|
||||
argument = null;
|
||||
|
||||
// Let's see whether the current token already has value, like "-o:value"
|
||||
|
||||
var a = _tokens[tokenIndex];
|
||||
if (a.HasValue)
|
||||
{
|
||||
a.MarkMatched();
|
||||
argument = a.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// OK, we may need to have to advance one or two tokens. Since we don't know
|
||||
// up front, we'll take a look ahead.
|
||||
|
||||
ArgumentToken lookahead;
|
||||
|
||||
// So, do we have a token?
|
||||
|
||||
if (!TryGetNextToken(tokenIndex, out lookahead))
|
||||
return false;
|
||||
|
||||
// If it's an option, then it's not an argument and we're done.
|
||||
|
||||
if (lookahead.IsOption)
|
||||
return false;
|
||||
|
||||
// OK, the lookahead is either a separator or it's an argument.
|
||||
|
||||
if (!lookahead.IsSeparator)
|
||||
{
|
||||
// If this is a flag, we need an explicit separator.
|
||||
// Since there is none, we don't consume this token.
|
||||
|
||||
if (isFlag)
|
||||
return false;
|
||||
|
||||
lookahead.MarkMatched();
|
||||
argument = lookahead.Name;
|
||||
tokenIndex++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Skip separator
|
||||
|
||||
lookahead.MarkMatched();
|
||||
tokenIndex++;
|
||||
|
||||
// See whether the next token is an argument.
|
||||
|
||||
if (!TryGetNextToken(tokenIndex, out lookahead))
|
||||
return false;
|
||||
|
||||
if (lookahead.IsOption || lookahead.IsSeparator)
|
||||
return false;
|
||||
|
||||
lookahead.MarkMatched();
|
||||
argument = lookahead.Name;
|
||||
tokenIndex++;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryGetNextToken(int tokenIndex, out ArgumentToken token)
|
||||
{
|
||||
if (++tokenIndex >= _tokens.Count)
|
||||
{
|
||||
token = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
token = _tokens[tokenIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
private static T ParseValue<T>(string diagnosticName, Func<string, T> valueConverter, string valueText)
|
||||
{
|
||||
try
|
||||
{
|
||||
return valueConverter(valueText);
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
var message = string.Format(Strings.CannotParseValueFmt, valueText, diagnosticName, ex.Message);
|
||||
throw new ArgumentSyntaxException(message);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetUnreadCommand()
|
||||
{
|
||||
return _tokens.Where(t => !t.IsOption && !t.IsSeparator).Select(t => t.Name).FirstOrDefault();
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> GetUnreadOptionNames()
|
||||
{
|
||||
return _tokens.Where(t => !t.IsMatched && t.IsOption).Select(t => t.Modifier + t.Name).ToArray();
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> GetUnreadParameters()
|
||||
{
|
||||
return _tokens.Where(t => !t.IsMatched && !t.IsOption).Select(t => t.ToString()).ToArray();
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> GetUnreadArguments()
|
||||
{
|
||||
return _tokens.Where(t => !t.IsMatched).Select(t => t.ToString()).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,424 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace System.CommandLine
|
||||
{
|
||||
public sealed partial class ArgumentSyntax
|
||||
{
|
||||
private readonly IEnumerable<string> _arguments;
|
||||
private readonly List<ArgumentCommand> _commands = new List<ArgumentCommand>();
|
||||
private readonly List<Argument> _options = new List<Argument>();
|
||||
private readonly List<Argument> _parameters = new List<Argument>();
|
||||
|
||||
private ArgumentParser _parser;
|
||||
private ArgumentCommand _definedCommand;
|
||||
private ArgumentCommand _activeCommand;
|
||||
|
||||
internal ArgumentSyntax(IEnumerable<string> arguments)
|
||||
{
|
||||
_arguments = arguments;
|
||||
|
||||
ApplicationName = GetApplicationName();
|
||||
HandleErrors = true;
|
||||
HandleHelp = true;
|
||||
HandleResponseFiles = true;
|
||||
}
|
||||
|
||||
public static ArgumentSyntax Parse(IEnumerable<string> arguments, Action<ArgumentSyntax> defineAction)
|
||||
{
|
||||
if (arguments == null)
|
||||
throw new ArgumentNullException("arguments");
|
||||
|
||||
if (defineAction == null)
|
||||
throw new ArgumentNullException("defineAction");
|
||||
|
||||
var syntax = new ArgumentSyntax(arguments);
|
||||
defineAction(syntax);
|
||||
syntax.Validate();
|
||||
return syntax;
|
||||
}
|
||||
|
||||
private void Validate()
|
||||
{
|
||||
// Check whether help is requested
|
||||
|
||||
if (HandleHelp && IsHelpRequested())
|
||||
{
|
||||
var helpText = GetHelpText();
|
||||
Console.Error.Write(helpText);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
// Check for invalid or missing command
|
||||
|
||||
if (_activeCommand == null && _commands.Any())
|
||||
{
|
||||
var unreadCommand = Parser.GetUnreadCommand();
|
||||
var message = unreadCommand == null
|
||||
? Strings.MissingCommand
|
||||
: string.Format(Strings.UnknownCommandFmt, unreadCommand);
|
||||
ReportError(message);
|
||||
}
|
||||
|
||||
// Check for invalid options and extra parameters
|
||||
|
||||
foreach (var option in Parser.GetUnreadOptionNames())
|
||||
{
|
||||
var message = string.Format(Strings.InvalidOptionFmt, option);
|
||||
ReportError(message);
|
||||
}
|
||||
|
||||
foreach (var parameter in Parser.GetUnreadParameters())
|
||||
{
|
||||
var message = string.Format(Strings.ExtraParameterFmt, parameter);
|
||||
ReportError(message);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsHelpRequested()
|
||||
{
|
||||
return Parser.GetUnreadOptionNames()
|
||||
.Any(a => string.Equals(a, @"-?", StringComparison.Ordinal) ||
|
||||
string.Equals(a, @"-h", StringComparison.Ordinal) ||
|
||||
string.Equals(a, @"--help", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
public void ReportError(string message)
|
||||
{
|
||||
if (HandleErrors)
|
||||
{
|
||||
Console.Error.WriteLine(Strings.ErrorWithMessageFmt, message);
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
throw new ArgumentSyntaxException(message);
|
||||
}
|
||||
|
||||
public ArgumentCommand<T> DefineCommand<T>(string name, T value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
throw new ArgumentException(Strings.NameMissing, "name");
|
||||
|
||||
if (!IsValidName(name))
|
||||
{
|
||||
var message = string.Format(Strings.CommandNameIsNotValidFmt, name);
|
||||
throw new ArgumentException(message, "name");
|
||||
}
|
||||
|
||||
if (_commands.Any(c => string.Equals(c.Name, name, StringComparison.Ordinal)))
|
||||
{
|
||||
var message = string.Format(Strings.CommandAlreadyDefinedFmt, name);
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
if (_options.Concat(_parameters).Any(c => c.Command == null))
|
||||
throw new InvalidOperationException(Strings.CannotDefineCommandsIfArgumentsExist);
|
||||
|
||||
var definedCommand = new ArgumentCommand<T>(name, value);
|
||||
_commands.Add(definedCommand);
|
||||
_definedCommand = definedCommand;
|
||||
|
||||
if (_activeCommand != null)
|
||||
return definedCommand;
|
||||
|
||||
if (!Parser.TryParseCommand(name))
|
||||
return definedCommand;
|
||||
|
||||
_activeCommand = _definedCommand;
|
||||
_activeCommand.MarkActive();
|
||||
|
||||
return definedCommand;
|
||||
}
|
||||
|
||||
public Argument<T> DefineOption<T>(string name, T defaultValue, Func<string, T> valueConverter)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
throw new ArgumentException(Strings.NameMissing, "name");
|
||||
|
||||
if (DefinedParameters.Any())
|
||||
throw new InvalidOperationException(Strings.OptionsMustBeDefinedBeforeParameters);
|
||||
|
||||
var names = ParseOptionNameList(name);
|
||||
var option = new Argument<T>(_definedCommand, names, defaultValue);
|
||||
_options.Add(option);
|
||||
|
||||
if (_activeCommand != _definedCommand)
|
||||
return option;
|
||||
|
||||
try
|
||||
{
|
||||
T value;
|
||||
if (Parser.TryParseOption(option.GetDisplayName(), option.Names, valueConverter, out value))
|
||||
option.SetValue(value);
|
||||
}
|
||||
catch (ArgumentSyntaxException ex)
|
||||
{
|
||||
ReportError(ex.Message);
|
||||
}
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
public ArgumentList<T> DefineOptionList<T>(string name, IReadOnlyList<T> defaultValue, Func<string, T> valueConverter)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
throw new ArgumentException(Strings.NameMissing, "name");
|
||||
|
||||
if (DefinedParameters.Any())
|
||||
throw new InvalidOperationException(Strings.OptionsMustBeDefinedBeforeParameters);
|
||||
|
||||
var names = ParseOptionNameList(name);
|
||||
var optionList = new ArgumentList<T>(_definedCommand, names, defaultValue);
|
||||
_options.Add(optionList);
|
||||
|
||||
if (_activeCommand != _definedCommand)
|
||||
return optionList;
|
||||
|
||||
try
|
||||
{
|
||||
IReadOnlyList<T> value;
|
||||
if (Parser.TryParseOptionList(optionList.GetDisplayName(), optionList.Names, valueConverter, out value))
|
||||
optionList.SetValue(value);
|
||||
}
|
||||
catch (ArgumentSyntaxException ex)
|
||||
{
|
||||
ReportError(ex.Message);
|
||||
}
|
||||
|
||||
return optionList;
|
||||
}
|
||||
|
||||
public Argument<T> DefineParameter<T>(string name, T defaultValue, Func<string, T> valueConverter)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
throw new ArgumentException(Strings.NameMissing, "name");
|
||||
|
||||
if (!IsValidName(name))
|
||||
{
|
||||
var message = string.Format(Strings.ParameterNameIsNotValidFmt, name);
|
||||
throw new ArgumentException(message, "name");
|
||||
}
|
||||
|
||||
if (DefinedParameters.Any(p => p.IsList))
|
||||
throw new InvalidOperationException(Strings.ParametersCannotBeDefinedAfterLists);
|
||||
|
||||
if (DefinedParameters.Any(p => string.Equals(name, p.Name, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
var message = string.Format(Strings.ParameterAlreadyDefinedFmt, name);
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
var parameter = new Argument<T>(_definedCommand, name, defaultValue);
|
||||
_parameters.Add(parameter);
|
||||
|
||||
if (_activeCommand != _definedCommand)
|
||||
return parameter;
|
||||
|
||||
try
|
||||
{
|
||||
T value;
|
||||
if (Parser.TryParseParameter(parameter.GetDisplayName(), valueConverter, out value))
|
||||
parameter.SetValue(value);
|
||||
}
|
||||
catch (ArgumentSyntaxException ex)
|
||||
{
|
||||
ReportError(ex.Message);
|
||||
}
|
||||
|
||||
return parameter;
|
||||
}
|
||||
|
||||
public ArgumentList<T> DefineParameterList<T>(string name, IReadOnlyList<T> defaultValue, Func<string, T> valueConverter)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
throw new ArgumentException(Strings.NameMissing, "name");
|
||||
|
||||
if (!IsValidName(name))
|
||||
{
|
||||
var message = string.Format(Strings.ParameterNameIsNotValidFmt, name);
|
||||
throw new ArgumentException(message, "name");
|
||||
}
|
||||
|
||||
if (DefinedParameters.Any(p => p.IsList))
|
||||
throw new InvalidOperationException(Strings.CannotDefineMultipleParameterLists);
|
||||
|
||||
var parameterList = new ArgumentList<T>(_definedCommand, name, defaultValue);
|
||||
_parameters.Add(parameterList);
|
||||
|
||||
if (_activeCommand != _definedCommand)
|
||||
return parameterList;
|
||||
|
||||
try
|
||||
{
|
||||
IReadOnlyList<T> values;
|
||||
if (Parser.TryParseParameterList(parameterList.GetDisplayName(), valueConverter, out values))
|
||||
parameterList.SetValue(values);
|
||||
}
|
||||
catch (ArgumentSyntaxException ex)
|
||||
{
|
||||
ReportError(ex.Message);
|
||||
}
|
||||
|
||||
return parameterList;
|
||||
}
|
||||
|
||||
private static bool IsValidName(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return false;
|
||||
|
||||
if (name[0] == '-')
|
||||
return false;
|
||||
|
||||
return name.All(c => char.IsLetterOrDigit(c) ||
|
||||
c == '-' ||
|
||||
c == '_');
|
||||
}
|
||||
|
||||
private IEnumerable<string> ParseOptionNameList(string name)
|
||||
{
|
||||
var names = name.Split('|').Select(n => n.Trim()).ToArray();
|
||||
|
||||
foreach (var alias in names)
|
||||
{
|
||||
if (!IsValidName(alias))
|
||||
{
|
||||
var message = string.Format(Strings.OptionNameIsNotValidFmt, alias);
|
||||
throw new ArgumentException(message, "name");
|
||||
}
|
||||
|
||||
foreach (var option in DefinedOptions)
|
||||
{
|
||||
if (option.Names.Any(n => string.Equals(n, alias, StringComparison.Ordinal)))
|
||||
{
|
||||
var message = string.Format(Strings.OptionAlreadyDefinedFmt, alias);
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
private IEnumerable<string> ParseResponseFile(string fileName)
|
||||
{
|
||||
if (!HandleResponseFiles)
|
||||
return null;
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
var message = string.Format(Strings.ResponseFileDoesNotExistFmt, fileName);
|
||||
ReportError(message);
|
||||
}
|
||||
|
||||
return File.ReadLines(fileName);
|
||||
}
|
||||
|
||||
private static string GetApplicationName()
|
||||
{
|
||||
var processPath = Environment.GetCommandLineArgs()[0];
|
||||
var processName = Path.GetFileNameWithoutExtension(processPath);
|
||||
return processName;
|
||||
}
|
||||
|
||||
public string ApplicationName { get; set; }
|
||||
|
||||
public bool HandleErrors { get; set; }
|
||||
|
||||
public bool HandleHelp { get; set; }
|
||||
|
||||
public bool HandleResponseFiles { get; set; }
|
||||
|
||||
private ArgumentParser Parser
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_parser == null)
|
||||
_parser = new ArgumentParser(_arguments, ParseResponseFile);
|
||||
|
||||
return _parser;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<Argument> DefinedOptions
|
||||
{
|
||||
get { return _options.Where(o => o.Command == null || o.Command == _definedCommand); }
|
||||
}
|
||||
|
||||
private IEnumerable<Argument> DefinedParameters
|
||||
{
|
||||
get { return _parameters.Where(p => p.Command == null || p.Command == _definedCommand); }
|
||||
}
|
||||
|
||||
public ArgumentCommand ActiveCommand
|
||||
{
|
||||
get { return _activeCommand; }
|
||||
}
|
||||
|
||||
public IReadOnlyList<ArgumentCommand> Commands
|
||||
{
|
||||
get { return _commands; }
|
||||
}
|
||||
|
||||
public IEnumerable<Argument> GetArguments()
|
||||
{
|
||||
return _options.Concat(_parameters);
|
||||
}
|
||||
|
||||
public IEnumerable<Argument> GetArguments(ArgumentCommand command)
|
||||
{
|
||||
return GetArguments().Where(c => c.Command == null || c.Command == command);
|
||||
}
|
||||
|
||||
public IEnumerable<Argument> GetOptions()
|
||||
{
|
||||
return _options;
|
||||
}
|
||||
|
||||
public IEnumerable<Argument> GetOptions(ArgumentCommand command)
|
||||
{
|
||||
return _options.Where(c => c.Command == null || c.Command == command);
|
||||
}
|
||||
|
||||
public IEnumerable<Argument> GetParameters()
|
||||
{
|
||||
return _parameters;
|
||||
}
|
||||
|
||||
public IEnumerable<Argument> GetParameters(ArgumentCommand command)
|
||||
{
|
||||
return _parameters.Where(c => c.Command == null || c.Command == command);
|
||||
}
|
||||
|
||||
public IEnumerable<Argument> GetActiveArguments()
|
||||
{
|
||||
return GetArguments(ActiveCommand);
|
||||
}
|
||||
|
||||
public IEnumerable<Argument> GetActiveOptions()
|
||||
{
|
||||
return GetOptions(ActiveCommand);
|
||||
}
|
||||
|
||||
public IEnumerable<Argument> GetActiveParameters()
|
||||
{
|
||||
return GetParameters(ActiveCommand);
|
||||
}
|
||||
|
||||
public string GetHelpText()
|
||||
{
|
||||
// TODO: This should use Console.WindowWidth but this API isn't available yet.
|
||||
// return GetHelpText(Console.WindowWidth - 2);
|
||||
return GetHelpText(72);
|
||||
}
|
||||
|
||||
public string GetHelpText(int maxWidth)
|
||||
{
|
||||
return HelpTextGenerator.Generate(this, maxWidth);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace System.CommandLine
|
||||
{
|
||||
public sealed class ArgumentSyntaxException : Exception
|
||||
{
|
||||
public ArgumentSyntaxException()
|
||||
{
|
||||
}
|
||||
|
||||
public ArgumentSyntaxException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ArgumentSyntaxException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,183 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace System.CommandLine
|
||||
{
|
||||
public partial class ArgumentSyntax
|
||||
{
|
||||
private static readonly Func<string, string> s_stringParser = v => v;
|
||||
private static readonly Func<string, bool> s_booleanParser = v => bool.Parse(v);
|
||||
private static readonly Func<string, int> s_int32Parser = v => int.Parse(v, CultureInfo.InvariantCulture);
|
||||
|
||||
// Commands
|
||||
|
||||
public ArgumentCommand<string> DefineCommand(string name)
|
||||
{
|
||||
return DefineCommand(name, name);
|
||||
}
|
||||
|
||||
public ArgumentCommand<T> DefineCommand<T>(string name, ref T command, T value, string help)
|
||||
{
|
||||
var result = DefineCommand(name, value);
|
||||
result.Help = help;
|
||||
|
||||
if (_activeCommand == result)
|
||||
command = value;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public ArgumentCommand<string> DefineCommand(string name, ref string value, string help)
|
||||
{
|
||||
return DefineCommand(name, ref value, name, help);
|
||||
}
|
||||
|
||||
// Options
|
||||
|
||||
public Argument<string> DefineOption(string name, string defaultValue)
|
||||
{
|
||||
return DefineOption(name, defaultValue, s_stringParser);
|
||||
}
|
||||
|
||||
public Argument<bool> DefineOption(string name, bool defaultValue)
|
||||
{
|
||||
return DefineOption(name, defaultValue, s_booleanParser);
|
||||
}
|
||||
|
||||
public Argument<int> DefineOption(string name, int defaultValue)
|
||||
{
|
||||
return DefineOption(name, defaultValue, s_int32Parser);
|
||||
}
|
||||
|
||||
public Argument<T> DefineOption<T>(string name, ref T value, Func<string, T> valueConverter, string help)
|
||||
{
|
||||
var option = DefineOption(name, value, valueConverter);
|
||||
option.Help = help;
|
||||
|
||||
value = option.Value;
|
||||
return option;
|
||||
}
|
||||
|
||||
public Argument<string> DefineOption(string name, ref string value, string help)
|
||||
{
|
||||
return DefineOption(name, ref value, s_stringParser, help);
|
||||
}
|
||||
|
||||
public Argument<bool> DefineOption(string name, ref bool value, string help)
|
||||
{
|
||||
return DefineOption(name, ref value, s_booleanParser, help);
|
||||
}
|
||||
|
||||
public Argument<int> DefineOption(string name, ref int value, string help)
|
||||
{
|
||||
return DefineOption(name, ref value, s_int32Parser, help);
|
||||
}
|
||||
|
||||
// Option lists
|
||||
|
||||
public ArgumentList<string> DefineOptionList(string name, IReadOnlyList<string> defaultValue)
|
||||
{
|
||||
return DefineOptionList(name, defaultValue, s_stringParser);
|
||||
}
|
||||
|
||||
public ArgumentList<int> DefineOptionList(string name, IReadOnlyList<int> defaultValue)
|
||||
{
|
||||
return DefineOptionList(name, defaultValue, s_int32Parser);
|
||||
}
|
||||
|
||||
public ArgumentList<T> DefineOptionList<T>(string name, ref IReadOnlyList<T> value, Func<string, T> valueConverter, string help)
|
||||
{
|
||||
var optionList = DefineOptionList(name, value, valueConverter);
|
||||
optionList.Help = help;
|
||||
|
||||
value = optionList.Value;
|
||||
return optionList;
|
||||
}
|
||||
|
||||
public ArgumentList<string> DefineOptionList(string name, ref IReadOnlyList<string> value, string help)
|
||||
{
|
||||
return DefineOptionList(name, ref value, s_stringParser, help);
|
||||
}
|
||||
|
||||
public ArgumentList<int> DefineOptionList(string name, ref IReadOnlyList<int> value, string help)
|
||||
{
|
||||
return DefineOptionList(name, ref value, s_int32Parser, help);
|
||||
}
|
||||
|
||||
// Parameters
|
||||
|
||||
public Argument<string> DefineParameter(string name, string defaultValue)
|
||||
{
|
||||
return DefineParameter(name, defaultValue, s_stringParser);
|
||||
}
|
||||
|
||||
public Argument<bool> DefineParameter(string name, bool defaultValue)
|
||||
{
|
||||
return DefineParameter(name, defaultValue, s_booleanParser);
|
||||
}
|
||||
|
||||
public Argument<int> DefineParameter(string name, int defaultValue)
|
||||
{
|
||||
return DefineParameter(name, defaultValue, s_int32Parser);
|
||||
}
|
||||
|
||||
public Argument<T> DefineParameter<T>(string name, ref T value, Func<string, T> valueConverter, string help)
|
||||
{
|
||||
var parameter = DefineParameter(name, value, valueConverter);
|
||||
parameter.Help = help;
|
||||
|
||||
value = parameter.Value;
|
||||
return parameter;
|
||||
}
|
||||
|
||||
public Argument<string> DefineParameter(string name, ref string value, string help)
|
||||
{
|
||||
return DefineParameter(name, ref value, s_stringParser, help);
|
||||
}
|
||||
|
||||
public Argument<bool> DefineParameter(string name, ref bool value, string help)
|
||||
{
|
||||
return DefineParameter(name, ref value, s_booleanParser, help);
|
||||
}
|
||||
|
||||
public Argument<int> DefineParameter(string name, ref int value, string help)
|
||||
{
|
||||
return DefineParameter(name, ref value, s_int32Parser, help);
|
||||
}
|
||||
|
||||
// Parameter list
|
||||
|
||||
public ArgumentList<string> DefineParameterList(string name, IReadOnlyList<string> defaultValue)
|
||||
{
|
||||
return DefineParameterList(name, defaultValue, s_stringParser);
|
||||
}
|
||||
|
||||
public ArgumentList<int> DefineParameterList(string name, IReadOnlyList<int> defaultValue)
|
||||
{
|
||||
return DefineParameterList(name, defaultValue, s_int32Parser);
|
||||
}
|
||||
|
||||
public ArgumentList<T> DefineParameterList<T>(string name, ref IReadOnlyList<T> value, Func<string, T> valueConverter, string help)
|
||||
{
|
||||
var parameterList = DefineParameterList(name, value, valueConverter);
|
||||
parameterList.Help = help;
|
||||
|
||||
value = parameterList.Value;
|
||||
return parameterList;
|
||||
}
|
||||
|
||||
public ArgumentList<string> DefineParameterList(string name, ref IReadOnlyList<string> value, string help)
|
||||
{
|
||||
return DefineParameterList(name, ref value, s_stringParser, help);
|
||||
}
|
||||
|
||||
public ArgumentList<int> DefineParameterList(string name, ref IReadOnlyList<int> value, string help)
|
||||
{
|
||||
return DefineParameterList(name, ref value, s_int32Parser, help);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace System.CommandLine
|
||||
{
|
||||
internal sealed class ArgumentToken
|
||||
{
|
||||
internal ArgumentToken(string modifier, string name, string value)
|
||||
{
|
||||
Modifier = modifier;
|
||||
Name = name;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public string Modifier { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public string Value { get; private set; }
|
||||
|
||||
public bool IsOption
|
||||
{
|
||||
get { return !string.IsNullOrEmpty(Modifier); }
|
||||
}
|
||||
|
||||
public bool IsSeparator
|
||||
{
|
||||
get { return Name == @":" || Name == @"="; }
|
||||
}
|
||||
|
||||
public bool HasValue
|
||||
{
|
||||
get { return !string.IsNullOrEmpty(Value); }
|
||||
}
|
||||
|
||||
public bool IsMatched { get; private set; }
|
||||
|
||||
public void MarkMatched()
|
||||
{
|
||||
IsMatched = true;
|
||||
}
|
||||
|
||||
private bool Equals(ArgumentToken other)
|
||||
{
|
||||
return string.Equals(Modifier, other.Modifier) &&
|
||||
string.Equals(Name, other.Name) &&
|
||||
string.Equals(Value, other.Value);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(obj, null))
|
||||
return false;
|
||||
|
||||
if (ReferenceEquals(obj, this))
|
||||
return true;
|
||||
|
||||
var other = obj as ArgumentToken;
|
||||
return !ReferenceEquals(other, null) && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = (Modifier != null ? Modifier.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ (Name != null ? Name.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ (Value != null ? Value.GetHashCode() : 0);
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return HasValue
|
||||
? string.Format(@"{0}{1}:{2}", Modifier, Name, Value)
|
||||
: string.Format(@"{0}{1}", Modifier, Name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace System.CommandLine
|
||||
{
|
||||
public sealed class Argument<T> : Argument
|
||||
{
|
||||
internal Argument(ArgumentCommand command, IEnumerable<string> names, T defaultValue)
|
||||
: base(command, names, true)
|
||||
{
|
||||
Value = defaultValue;
|
||||
Value = DefaultValue = defaultValue;
|
||||
}
|
||||
|
||||
internal Argument(ArgumentCommand command, string name, T defaultValue)
|
||||
: base(command, new[] { name }, false)
|
||||
{
|
||||
Value = defaultValue;
|
||||
DefaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public new T Value { get; private set; }
|
||||
|
||||
public new T DefaultValue { get; private set; }
|
||||
|
||||
public override bool IsFlag
|
||||
{
|
||||
get { return typeof(T) == typeof(bool); }
|
||||
}
|
||||
|
||||
internal override object GetValue()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
internal override object GetDefaultValue()
|
||||
{
|
||||
return DefaultValue;
|
||||
}
|
||||
|
||||
internal void SetValue(T value)
|
||||
{
|
||||
Value = value;
|
||||
MarkSpecified();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.CommandLine
|
||||
{
|
||||
// Low level replacement of LINQ Enumerable class. In particular, this implementation
|
||||
// doesn't use generic virtual methods.
|
||||
// System.CommandLine needs to be usable for small .NET Native apps that don't carry the
|
||||
// overhead of expensive runtime features such as the generic virtual methods.
|
||||
internal static class Enumerable
|
||||
{
|
||||
public static IEnumerable<T> Empty<T>()
|
||||
{
|
||||
return Linq.Enumerable.Empty<T>();
|
||||
}
|
||||
|
||||
public static IEnumerable<U> Select<T, U>(this IEnumerable<T> values, Func<T, U> func)
|
||||
{
|
||||
Debug.Assert(values != null);
|
||||
|
||||
foreach (T value in values)
|
||||
{
|
||||
yield return func(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Where<T>(this IEnumerable<T> values, Func<T, bool> func)
|
||||
{
|
||||
Debug.Assert(values != null);
|
||||
|
||||
foreach (T value in values)
|
||||
{
|
||||
if (func(value))
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Concat<T>(this IEnumerable<T> first, IEnumerable<T> second)
|
||||
{
|
||||
return Linq.Enumerable.Concat(first, second);
|
||||
}
|
||||
|
||||
public static bool All<T>(this IEnumerable<T> source, Func<T, bool> predicate)
|
||||
{
|
||||
return Linq.Enumerable.All(source, predicate);
|
||||
}
|
||||
|
||||
public static bool Any<T>(this IEnumerable<T> source, Func<T, bool> predicate)
|
||||
{
|
||||
return Linq.Enumerable.Any(source, predicate);
|
||||
}
|
||||
|
||||
public static bool Any<T>(this IEnumerable<T> source)
|
||||
{
|
||||
return Linq.Enumerable.Any(source);
|
||||
}
|
||||
|
||||
public static T[] ToArray<T>(this IEnumerable<T> source)
|
||||
{
|
||||
return Linq.Enumerable.ToArray(source);
|
||||
}
|
||||
|
||||
public static T Last<T>(this IEnumerable<T> source)
|
||||
{
|
||||
return Linq.Enumerable.Last(source);
|
||||
}
|
||||
|
||||
public static T FirstOrDefault<T>(this IEnumerable<T> source)
|
||||
{
|
||||
return Linq.Enumerable.FirstOrDefault(source);
|
||||
}
|
||||
|
||||
public static T First<T>(this IEnumerable<T> source)
|
||||
{
|
||||
return Linq.Enumerable.First(source);
|
||||
}
|
||||
|
||||
public static int Max(this IEnumerable<int> source)
|
||||
{
|
||||
return Linq.Enumerable.Max(source);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,260 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace System.CommandLine
|
||||
{
|
||||
internal static class HelpTextGenerator
|
||||
{
|
||||
public static string Generate(ArgumentSyntax argumentSyntax, int maxWidth)
|
||||
{
|
||||
var forCommandList = argumentSyntax.ActiveCommand == null &&
|
||||
argumentSyntax.Commands.Any();
|
||||
|
||||
var page = forCommandList
|
||||
? GetCommandListHelp(argumentSyntax)
|
||||
: GetCommandHelp(argumentSyntax, argumentSyntax.ActiveCommand);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.WriteHelpPage(page, maxWidth);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private struct HelpPage
|
||||
{
|
||||
public string ApplicationName;
|
||||
public IEnumerable<string> SyntaxElements;
|
||||
public IReadOnlyList<HelpRow> Rows;
|
||||
}
|
||||
|
||||
private struct HelpRow
|
||||
{
|
||||
public string Header;
|
||||
public string Text;
|
||||
}
|
||||
|
||||
private static void WriteHelpPage(this StringBuilder sb, HelpPage page, int maxWidth)
|
||||
{
|
||||
sb.WriteUsage(page.ApplicationName, page.SyntaxElements, maxWidth);
|
||||
|
||||
if (!page.Rows.Any())
|
||||
return;
|
||||
|
||||
sb.AppendLine();
|
||||
|
||||
sb.WriteRows(page.Rows, maxWidth);
|
||||
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
private static void WriteUsage(this StringBuilder sb, string applicationName, IEnumerable<string> syntaxElements, int maxWidth)
|
||||
{
|
||||
var usageHeader = string.Format(Strings.HelpUsageOfApplicationFmt, applicationName);
|
||||
sb.Append(usageHeader);
|
||||
|
||||
if (syntaxElements.Any())
|
||||
sb.Append(@" ");
|
||||
|
||||
var syntaxIndent = usageHeader.Length + 1;
|
||||
var syntaxMaxWidth = maxWidth - syntaxIndent;
|
||||
|
||||
sb.WriteWordWrapped(syntaxElements, syntaxIndent, syntaxMaxWidth);
|
||||
}
|
||||
|
||||
private static void WriteRows(this StringBuilder sb, IReadOnlyList<HelpRow> rows, int maxWidth)
|
||||
{
|
||||
const int indent = 4;
|
||||
var maxColumnWidth = rows.Select(r => r.Header.Length).Max();
|
||||
var helpStartColumn = maxColumnWidth + 2 * indent;
|
||||
|
||||
var maxHelpWidth = maxWidth - helpStartColumn;
|
||||
if (maxHelpWidth < 0)
|
||||
maxHelpWidth = maxWidth;
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var headerStart = sb.Length;
|
||||
|
||||
sb.Append(' ', indent);
|
||||
sb.Append(row.Header);
|
||||
|
||||
var headerLength = sb.Length - headerStart;
|
||||
var requiredSpaces = helpStartColumn - headerLength;
|
||||
|
||||
sb.Append(' ', requiredSpaces);
|
||||
|
||||
var words = SplitWords(row.Text);
|
||||
sb.WriteWordWrapped(words, helpStartColumn, maxHelpWidth);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteWordWrapped(this StringBuilder sb, IEnumerable<string> words, int indent, int maxidth)
|
||||
{
|
||||
var helpLines = WordWrapLines(words, maxidth);
|
||||
var isFirstHelpLine = true;
|
||||
|
||||
foreach (var helpLine in helpLines)
|
||||
{
|
||||
if (isFirstHelpLine)
|
||||
isFirstHelpLine = false;
|
||||
else
|
||||
sb.Append(' ', indent);
|
||||
|
||||
sb.AppendLine(helpLine);
|
||||
}
|
||||
|
||||
if (isFirstHelpLine)
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
private static HelpPage GetCommandListHelp(ArgumentSyntax argumentSyntax)
|
||||
{
|
||||
return new HelpPage
|
||||
{
|
||||
ApplicationName = argumentSyntax.ApplicationName,
|
||||
SyntaxElements = GetGlobalSyntax(),
|
||||
Rows = GetCommandRows(argumentSyntax).ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
private static HelpPage GetCommandHelp(ArgumentSyntax argumentSyntax, ArgumentCommand command)
|
||||
{
|
||||
return new HelpPage
|
||||
{
|
||||
ApplicationName = argumentSyntax.ApplicationName,
|
||||
SyntaxElements = GetCommandSyntax(argumentSyntax, command),
|
||||
Rows = GetArgumentRows(argumentSyntax, command).ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetGlobalSyntax()
|
||||
{
|
||||
yield return @"<command>";
|
||||
yield return @"[<args>]";
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetCommandSyntax(ArgumentSyntax argumentSyntax, ArgumentCommand command)
|
||||
{
|
||||
if (command != null)
|
||||
yield return command.Name;
|
||||
|
||||
foreach (var option in argumentSyntax.GetOptions(command).Where(o => !o.IsHidden))
|
||||
yield return GetOptionSyntax(option);
|
||||
|
||||
if (argumentSyntax.GetParameters(command).All(p => p.IsHidden))
|
||||
yield break;
|
||||
|
||||
if (argumentSyntax.GetOptions(command).Any(o => !o.IsHidden))
|
||||
yield return @"[--]";
|
||||
|
||||
foreach (var parameter in argumentSyntax.GetParameters(command).Where(o => !o.IsHidden))
|
||||
yield return GetParameterSyntax(parameter);
|
||||
}
|
||||
|
||||
private static string GetOptionSyntax(Argument option)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.Append(@"[");
|
||||
sb.Append(option.GetDisplayName());
|
||||
|
||||
if (!option.IsFlag)
|
||||
sb.Append(@" <arg>");
|
||||
|
||||
if (option.IsList)
|
||||
sb.Append(@"...");
|
||||
|
||||
sb.Append(@"]");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GetParameterSyntax(Argument parameter)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.Append(parameter.GetDisplayName());
|
||||
if (parameter.IsList)
|
||||
sb.Append(@"...");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static IEnumerable<HelpRow> GetCommandRows(ArgumentSyntax argumentSyntax)
|
||||
{
|
||||
return argumentSyntax.Commands
|
||||
.Where(c => !c.IsHidden)
|
||||
.Select(c => new HelpRow { Header = c.Name, Text = c.Help });
|
||||
}
|
||||
|
||||
private static IEnumerable<HelpRow> GetArgumentRows(ArgumentSyntax argumentSyntax, ArgumentCommand command)
|
||||
{
|
||||
return argumentSyntax.GetArguments(command)
|
||||
.Where(a => !a.IsHidden)
|
||||
.Select(a => new HelpRow { Header = GetArgumentRowHeader(a), Text = a.Help });
|
||||
}
|
||||
|
||||
private static string GetArgumentRowHeader(Argument argument)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
foreach (var displayName in argument.GetDisplayNames())
|
||||
{
|
||||
if (sb.Length > 0)
|
||||
sb.Append(@", ");
|
||||
|
||||
sb.Append(displayName);
|
||||
}
|
||||
|
||||
if (argument.IsOption && !argument.IsFlag)
|
||||
sb.Append(@" <arg>");
|
||||
|
||||
if (argument.IsList)
|
||||
sb.Append(@"...");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static IEnumerable<string> WordWrapLines(IEnumerable<string> tokens, int maxWidth)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
foreach (var token in tokens)
|
||||
{
|
||||
var newLength = sb.Length == 0
|
||||
? token.Length
|
||||
: sb.Length + 1 + token.Length;
|
||||
|
||||
if (newLength > maxWidth)
|
||||
{
|
||||
if (sb.Length == 0)
|
||||
{
|
||||
yield return token;
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return sb.ToString();
|
||||
sb.Clear();
|
||||
}
|
||||
|
||||
if (sb.Length > 0)
|
||||
sb.Append(@" ");
|
||||
|
||||
sb.Append(token);
|
||||
}
|
||||
|
||||
if (sb.Length > 0)
|
||||
yield return sb.ToString();
|
||||
}
|
||||
|
||||
private static IEnumerable<string> SplitWords(string text)
|
||||
{
|
||||
return string.IsNullOrEmpty(text)
|
||||
? Enumerable.Empty<string>()
|
||||
: text.Split(' ');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("System.CommandLine.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
|
|
@ -1,253 +0,0 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System {
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Strings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Strings() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Strings", typeof(Strings).GetTypeInfo().Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot define commands if global options or parameters exist..
|
||||
/// </summary>
|
||||
internal static string CannotDefineCommandsIfArgumentsExist {
|
||||
get {
|
||||
return ResourceManager.GetString("CannotDefineCommandsIfArgumentsExist", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot define multiple parameter lists..
|
||||
/// </summary>
|
||||
internal static string CannotDefineMultipleParameterLists {
|
||||
get {
|
||||
return ResourceManager.GetString("CannotDefineMultipleParameterLists", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to value '{0}' isn't valid for {1}: {2}.
|
||||
/// </summary>
|
||||
internal static string CannotParseValueFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("CannotParseValueFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Command '{0}' is already defined..
|
||||
/// </summary>
|
||||
internal static string CommandAlreadyDefinedFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("CommandAlreadyDefinedFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Name '{0}' cannot be used for a command..
|
||||
/// </summary>
|
||||
internal static string CommandNameIsNotValidFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("CommandNameIsNotValidFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to error: {0}.
|
||||
/// </summary>
|
||||
internal static string ErrorWithMessageFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorWithMessageFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to extra parameter '{0}'.
|
||||
/// </summary>
|
||||
internal static string ExtraParameterFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("ExtraParameterFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to usage: {0}.
|
||||
/// </summary>
|
||||
internal static string HelpUsageOfApplicationFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("HelpUsageOfApplicationFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to invalid option {0}.
|
||||
/// </summary>
|
||||
internal static string InvalidOptionFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidOptionFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to missing command.
|
||||
/// </summary>
|
||||
internal static string MissingCommand {
|
||||
get {
|
||||
return ResourceManager.GetString("MissingCommand", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You must specify a name..
|
||||
/// </summary>
|
||||
internal static string NameMissing {
|
||||
get {
|
||||
return ResourceManager.GetString("NameMissing", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Option '{0}' is already defined..
|
||||
/// </summary>
|
||||
internal static string OptionAlreadyDefinedFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("OptionAlreadyDefinedFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Name '{0}' cannot be used for an option..
|
||||
/// </summary>
|
||||
internal static string OptionNameIsNotValidFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("OptionNameIsNotValidFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to option {0} requires a value.
|
||||
/// </summary>
|
||||
internal static string OptionRequiresValueFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("OptionRequiresValueFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Options must be defined before any parameters..
|
||||
/// </summary>
|
||||
internal static string OptionsMustBeDefinedBeforeParameters {
|
||||
get {
|
||||
return ResourceManager.GetString("OptionsMustBeDefinedBeforeParameters", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Parameter '{0}' is already defined..
|
||||
/// </summary>
|
||||
internal static string ParameterAlreadyDefinedFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("ParameterAlreadyDefinedFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Name '{0}' cannot be used for a parameter..
|
||||
/// </summary>
|
||||
internal static string ParameterNameIsNotValidFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("ParameterNameIsNotValidFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Parameters cannot be defined after parameter lists..
|
||||
/// </summary>
|
||||
internal static string ParametersCannotBeDefinedAfterLists {
|
||||
get {
|
||||
return ResourceManager.GetString("ParametersCannotBeDefinedAfterLists", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Response file '{0}' doesn't exist..
|
||||
/// </summary>
|
||||
internal static string ResponseFileDoesNotExistFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("ResponseFileDoesNotExistFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to unknown command '{0}'.
|
||||
/// </summary>
|
||||
internal static string UnknownCommandFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("UnknownCommandFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Unmatched quote at position {0}..
|
||||
/// </summary>
|
||||
internal static string UnmatchedQuoteFmt {
|
||||
get {
|
||||
return ResourceManager.GetString("UnmatchedQuoteFmt", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,183 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="CannotDefineCommandsIfArgumentsExist" xml:space="preserve">
|
||||
<value>Cannot define commands if global options or parameters exist.</value>
|
||||
</data>
|
||||
<data name="CannotParseValueFmt" xml:space="preserve">
|
||||
<value>value '{0}' isn't valid for {1}: {2}</value>
|
||||
</data>
|
||||
<data name="CommandAlreadyDefinedFmt" xml:space="preserve">
|
||||
<value>Command '{0}' is already defined.</value>
|
||||
</data>
|
||||
<data name="ErrorWithMessageFmt" xml:space="preserve">
|
||||
<value>error: {0}</value>
|
||||
</data>
|
||||
<data name="ExtraParameterFmt" xml:space="preserve">
|
||||
<value>extra parameter '{0}'</value>
|
||||
</data>
|
||||
<data name="HelpUsageOfApplicationFmt" xml:space="preserve">
|
||||
<value>usage: {0}</value>
|
||||
</data>
|
||||
<data name="InvalidOptionFmt" xml:space="preserve">
|
||||
<value>invalid option {0}</value>
|
||||
</data>
|
||||
<data name="MissingCommand" xml:space="preserve">
|
||||
<value>missing command</value>
|
||||
</data>
|
||||
<data name="NameMissing" xml:space="preserve">
|
||||
<value>You must specify a name.</value>
|
||||
</data>
|
||||
<data name="OptionAlreadyDefinedFmt" xml:space="preserve">
|
||||
<value>Option '{0}' is already defined.</value>
|
||||
</data>
|
||||
<data name="OptionsMustBeDefinedBeforeParameters" xml:space="preserve">
|
||||
<value>Options must be defined before any parameters.</value>
|
||||
</data>
|
||||
<data name="ResponseFileDoesNotExistFmt" xml:space="preserve">
|
||||
<value>Response file '{0}' doesn't exist.</value>
|
||||
</data>
|
||||
<data name="UnknownCommandFmt" xml:space="preserve">
|
||||
<value>unknown command '{0}'</value>
|
||||
</data>
|
||||
<data name="UnmatchedQuoteFmt" xml:space="preserve">
|
||||
<value>Unmatched quote at position {0}.</value>
|
||||
</data>
|
||||
<data name="OptionRequiresValueFmt" xml:space="preserve">
|
||||
<value>option {0} requires a value</value>
|
||||
</data>
|
||||
<data name="ParameterAlreadyDefinedFmt" xml:space="preserve">
|
||||
<value>Parameter '{0}' is already defined.</value>
|
||||
</data>
|
||||
<data name="CannotDefineMultipleParameterLists" xml:space="preserve">
|
||||
<value>Cannot define multiple parameter lists.</value>
|
||||
</data>
|
||||
<data name="ParametersCannotBeDefinedAfterLists" xml:space="preserve">
|
||||
<value>Parameters cannot be defined after parameter lists.</value>
|
||||
</data>
|
||||
<data name="CommandNameIsNotValidFmt" xml:space="preserve">
|
||||
<value>Name '{0}' cannot be used for a command.</value>
|
||||
</data>
|
||||
<data name="OptionNameIsNotValidFmt" xml:space="preserve">
|
||||
<value>Name '{0}' cannot be used for an option.</value>
|
||||
</data>
|
||||
<data name="ParameterNameIsNotValidFmt" xml:space="preserve">
|
||||
<value>Name '{0}' cannot be used for a parameter.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1,38 +1,30 @@
|
|||
using System;
|
||||
//using System.CommandLine;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var format = "Zip";
|
||||
//var pkgName = "";
|
||||
|
||||
//ArgumentSyntax.Parse(args, syntax =>
|
||||
//{
|
||||
//syntax.DefineOption("f|format", ref format, "The format that you want to pack in. Currently valid: zip");
|
||||
//syntax.DefineOption("o|output", ref pkgName, "The resulting package name");
|
||||
//});
|
||||
|
||||
var shellOutCommand = "dotnet";
|
||||
//var arguments = $"msbuild /t:Packer /p:PackageName={pkgName} /p:Format={format} /v:Quiet";
|
||||
var arguments = $"msbuild /t:Packer /p:Format={format}";
|
||||
|
||||
var psi = new ProcessStartInfo
|
||||
namespace dotnetpacker
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
FileName = shellOutCommand,
|
||||
Arguments = arguments
|
||||
};
|
||||
|
||||
var process = new Process
|
||||
{
|
||||
StartInfo = psi,
|
||||
var msbArguments = $"msbuild /t:Packer /p:Format=zip /v:m";
|
||||
|
||||
};
|
||||
|
||||
var rcode = process.Start();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = "dotnet",
|
||||
Arguments = msbArguments
|
||||
};
|
||||
|
||||
var process = new Process
|
||||
{
|
||||
StartInfo = psi,
|
||||
|
||||
};
|
||||
|
||||
var rcode = process.Start();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>0a4d9107-c593-4afb-ab17-559324d2ecb4</ProjectGuid>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
dotnet restore
|
||||
dotnet build
|
||||
cd dotnet-packer
|
||||
dotnet pack -o ../nupkgs
|
||||
cd ../SampleTargets.PackerTarget
|
||||
dotnet pack -o ../nupkgs
|
||||
cd ..
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
dotnet restore
|
||||
dotnet build
|
||||
cd dotnet-packer
|
||||
dotnet pack -o ../nupkgs
|
||||
cd ../SampleTargets.PackerTarget
|
||||
dotnet pack -o ../nupkgs
|
||||
cd ..
|
|
@ -2,7 +2,6 @@
|
|||
<configuration>
|
||||
<packageSources>
|
||||
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
|
||||
<add key="localFeed" value="../../SampleTargets.PackerTarget/" />
|
||||
<add key="localFeed2" value="../../dotnet-packer" />
|
||||
<add key="localFeed" value="../../nupkgs" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
|
|
Загрузка…
Ссылка в новой задаче