From a2449ae8aefb0c95f618904a95ba3200eeeb8ae3 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 12 May 2023 13:49:31 +0300 Subject: [PATCH] cli: autocomplete output filenames for `contract compile` cmd It's enough to specify the input file only to get the standard output: ``` $ neo-go contract compile -i ./1-print/1-print.go $ neo-go contract compile -i ./1-print/ ``` Signed-off-by: Anna Shaleva --- cli/smartcontract/contract_test.go | 16 +++++++++++- cli/smartcontract/smart_contract.go | 38 ++++++++++++++++++++++++++--- docs/compiler.md | 8 ++++++ 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/cli/smartcontract/contract_test.go b/cli/smartcontract/contract_test.go index 917f07221..bb41a4a30 100644 --- a/cli/smartcontract/contract_test.go +++ b/cli/smartcontract/contract_test.go @@ -237,7 +237,8 @@ func TestContractInitAndCompile(t *testing.T) { e.RunWithError(t, "neo-go", "contract", "init", "--name", ctrPath) }) - srcPath := filepath.Join(ctrPath, "main.go") + ctrRootPath := filepath.Join(ctrPath, "main") + srcPath := ctrRootPath + ".go" cfgPath := filepath.Join(ctrPath, "neo-go.yml") nefPath := filepath.Join(tmpDir, "testcontract.nef") manifestPath := filepath.Join(tmpDir, "testcontract.manifest.json") @@ -288,6 +289,19 @@ func TestContractInitAndCompile(t *testing.T) { e.Run(t, append(cmd, "--verbose")...) e.CheckNextLine(t, "^[0-9a-hA-H]+$") }) + + t.Run("autocomplete outputs", func(t *testing.T) { + cfg, err := os.ReadFile(cfgPath) + require.NoError(t, err) + require.NoError(t, os.WriteFile(filepath.Join(ctrPath, "main.yml"), cfg, os.ModePerm)) + e.Run(t, "neo-go", "contract", "compile", "--in", srcPath) + defaultNefPath := ctrRootPath + ".nef" + defaultManifestPath := ctrRootPath + ".manifest.json" + defaultBindingsPath := ctrRootPath + ".bindings.yml" + require.FileExists(t, defaultNefPath) + require.FileExists(t, defaultManifestPath) + require.FileExists(t, defaultBindingsPath) + }) } // Checks that error is returned if GAS available for test-invoke exceeds diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index b478f076f..5a2af4685 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -126,11 +126,18 @@ func NewCommands() []cli.Command { Name: "compile", Usage: "compile a smart contract to a .nef file", UsageText: "neo-go contract compile -i path [-o nef] [-v] [-d] [-m manifest] [-c yaml] [--bindings file] [--no-standards] [--no-events] [--no-permissions]", - Action: contractCompile, + Description: `Compiles given smart contract to a .nef file and emits other associated + information (manifest, bindings configuration, debug information files) if + asked to. If none of --out, --manifest, --config, --bindings flags are specified, + then the output filenames for these flags will be guessed using the contract + name or path provided via --in option by trimming/adding corresponding suffixes + to the common part of the path. In the latter case the configuration filepath + will be guessed from the --in option using the same rule."`, + Action: contractCompile, Flags: []cli.Flag{ cli.StringFlag{ Name: "in, i", - Usage: "Input file for the smart contract to be compiled", + Usage: "Input file for the smart contract to be compiled (*.go file or directory)", }, cli.StringFlag{ Name: "out, o", @@ -400,13 +407,38 @@ func contractCompile(ctx *cli.Context) error { manifestFile := ctx.String("manifest") confFile := ctx.String("config") debugFile := ctx.String("debug") + out := ctx.String("out") bindings := ctx.String("bindings") if len(confFile) == 0 && (len(manifestFile) != 0 || len(debugFile) != 0 || len(bindings) != 0) { return cli.NewExitError(errNoConfFile, 1) } + autocomplete := len(manifestFile) == 0 && + len(confFile) == 0 && + len(out) == 0 && + len(bindings) == 0 + if autocomplete { + var root string + fileInfo, err := os.Stat(src) + if err != nil { + return cli.NewExitError(fmt.Errorf("failed to stat source file or directory: %w", err), 1) + } + if fileInfo.IsDir() { + base := filepath.Base(fileInfo.Name()) + if base == string(filepath.Separator) { + base = "contract" + } + root = filepath.Join(src, base) + } else { + root = strings.TrimSuffix(src, ".go") + } + manifestFile = root + ".manifest.json" + confFile = root + ".yml" + out = root + ".nef" + bindings = root + ".bindings.yml" + } o := &compiler.Options{ - Outfile: ctx.String("out"), + Outfile: out, DebugInfo: debugFile, ManifestFile: manifestFile, diff --git a/docs/compiler.md b/docs/compiler.md index a38a2dcf6..d4d17efb4 100644 --- a/docs/compiler.md +++ b/docs/compiler.md @@ -80,6 +80,14 @@ $ go mod tidy By default, the filename will be the name of your .go file with the .nef extension, the file will be located in the same directory with your Go contract. +Along with the compiled contract and if the contract configuration file +`contract.yml` exist, the following files will be generated: +* smart-contract manifest file (`contract.manifest.json`) that is needed to deploy + the contract to the network +* bindings configuration file (`contract.bindings.yml`) that is needed to generate + code-based or RPC contract bindings +All of them will be located in the same directory with your Go contract. + If you want another location for your compiled contract: ```