Skip to content

Commit

Permalink
Merge pull request #12 from metal3d/develop
Browse files Browse the repository at this point in the history
Enhancements and refactorization
  • Loading branch information
metal3d authored Jun 23, 2023
2 parents 0604630 + c342bb9 commit b90ca79
Show file tree
Hide file tree
Showing 8 changed files with 772 additions and 180 deletions.
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ [email protected]

TARGETS=dist/goreorder-linux-amd64 dist/goreorder-darwin-amd64 dist/goreorder-windows-amd64.exe dist/goreorder-freebsd-amd64

# TEST selection
# e.g. TEST="TestFoo TestBar"
TEST=

install:
go install -v $(CC_OPTS) $(PACKAGE)

Expand Down Expand Up @@ -80,8 +84,12 @@ clean-dist:
clean: clean-dist
rm -f ./goreorder

test:
test:
ifeq ($(strip $(TEST)),)
go test -v -race -cover -short ./...
else
go test -v -race -run $(TEST) ./...
endif

test-cover-html:
go test -cover -coverprofile=coverage.out ./...
Expand Down
48 changes: 45 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ This tool will "reorder" your sources:

There are several possibilities:

- If you have "go" on your machine, simply install using `go install github.com/metal3d/goreorder@latest` (you can replace "latest" by a known version)
- Visit the [release page](https://github.com/metal3d/goreorder/releases) to download the latest version (to place un you `$PATH`)
- If you have "go" on your machine, simply install using (you can replace "latest" by a known tag):
```bash
go install github.com/metal3d/goreorder/cmd/goreorder@latest`
```
- Visit the [release page](https://github.com/metal3d/goreorder/releases) to download the desired version (to place un you `$PATH`)
- Use the installer:
```bash
curl -sSL https://raw.githubusercontent.com/metal3d/goreorder/main/repo-tools/install.sh | bash -s
Expand All @@ -32,7 +35,7 @@ cd goreorder
make install
```

## Basic Usage
# Basic Usage

```
goreorder reorders the structs (optional), methods and constructors in a Go
Expand Down Expand Up @@ -78,8 +81,47 @@ patch -p1 < ./reorder.patch
patch -p1 -R < ./reorder.patch
```

# Releases are GPG signed

The released binaries are signed with GPG. If you want to verify that the release comes from this repository and was built by the author:

```bash
## Optional, you can get and trust the owner GPG key
# this is the repo owner key:
_KEY="F3702E3FAD8F76DC"
# You can get it with this command:
_KEY=$(curl -s https://api.github.com/users/metal3d/gpg_keys | \
awk -F'"' '/"key_id"/{print $4; exit}')
echo ${_KEY}
# you can import the repository owner key from keyserver
gpg --keyserver hkps://keys.openpgp.org/ --recv-keys ${_KEY}
# optoinal, trust owner key
_FPR=$(gpg -k --with-colons --fingerprint "${_KEY}" | awk -F: '/fpr/{print $10; exit}')
echo ${_FPR}:6: | gpg --import-ownertrust
unset _KEY _FPR
## Verification
# get the signature of the right binary
_REL="goreorder-linux-amd64"
_SIGNURL=https://github.com/metal3d/goreorder/releases/download/${_REL}.asc
curl ${_SIGNURL} -o /tmp/goreorder.asc
unset _SIGNURL _REL
# get or set the path to the binary file you downloaded / installed
# _GOREORDERBIN=/path/to/the/binary
_GOREORDERBIN=$(command -v goreorder)
# check the signature
gpg --verify /tmp/goreorder.asc $_GOREORDERBIN
rm /tmp/goreorder.asc
```

# Contribute

Please fill an issue to create a bug report.

If you want to participate, please fork the repository and propose a pull request **on the "develop" branch**.

16 changes: 14 additions & 2 deletions cmd/goreorder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,13 @@ func processFile(fileOrDirectoryName string, formatToolName string, reorderStruc

if input != nil && len(input) != 0 {
// process stdin
content, err := ordering.ReorderSource(fileOrDirectoryName, formatToolName, reorderStructs, input, diff)
content, err := ordering.ReorderSource(ordering.ReorderConfig{
Filename: fileOrDirectoryName,
FormatCommand: formatToolName,
ReorderStructs: reorderStructs,
Src: input,
Diff: diff,
})
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -213,7 +219,13 @@ func processFile(fileOrDirectoryName string, formatToolName string, reorderStruc
}

log.Println("Processing file: " + fileOrDirectoryName)
output, err := ordering.ReorderSource(fileOrDirectoryName, formatToolName, reorderStructs, input, diff)
output, err := ordering.ReorderSource(ordering.ReorderConfig{
Filename: fileOrDirectoryName,
FormatCommand: formatToolName,
ReorderStructs: reorderStructs,
Src: input,
Diff: diff,
})
if err != nil {
log.Println("ERR: Ordering error:", err)
return
Expand Down
155 changes: 109 additions & 46 deletions ordering/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,60 +4,74 @@ import (
"crypto/sha256"
"errors"
"fmt"
"go/format"
"io/ioutil"
"os"
"os/exec"
"sort"
"strings"
)

// ReorderSource reorders the source code in the given filename. It will be helped by the formatCommand (gofmt or goimports). The method is to
// use the Parse() function to extract types, methods and constructors. Then we replace the original source code with a comment containing the
// sha256 of the source. This is made to not lose the original source code "lenght" while we reinject the ordered source code. Then, we finally
type ReorderConfig struct {
Filename string
FormatCommand string
ReorderStructs bool
Diff bool
Src interface{}
}

// ReorderSource reorders the source code in the given filename.
// It will be helped by the formatCommand (gofmt or goimports).
// If gofmt is used, the source code will be formatted with the go/fmt package in memory.
//
// This function calls the Parse() function to extract types, methods, vars, consts and constructors.
// Then it replaces the original source code with a comment containing the
// sha256 of the source. This is made to not lose the original source code "lenght"
// while we reinject the ordered source code. Then, we finally
// remove thses lines from the source code.
func ReorderSource(filename, formatCommand string, reorderStructs bool, src interface{}, diff bool) (string, error) {
// in all cases, we must return the original source code if an error occurs
// get the content of the file
func ReorderSource(opt ReorderConfig) (string, error) {
var content []byte
var err error
if src == nil || len(src.([]byte)) == 0 {
content, err = ioutil.ReadFile(filename)
if opt.Src == nil || len(opt.Src.([]byte)) == 0 {
content, err = ioutil.ReadFile(opt.Filename)
if err != nil {
return "", err
}
} else {
content = src.([]byte)
content = opt.Src.([]byte)
}

methods, constructors, structs, err := Parse(filename, formatCommand, content)
info, err := Parse(opt.Filename, content)

if err != nil {
return string(content), errors.New("Error parsing source: " + err.Error())
}

if len(structs) == 0 {
return string(content), errors.New("No structs found in " + filename + ", cannot reorder")
if len(info.Structs) == 0 {
return string(content), errors.New("No structs found in " + opt.Filename + ", cannot reorder")
}

// sort methods by name
for _, method := range methods {
for _, method := range info.Methods {
sort.Slice(method, func(i, j int) bool {
return method[i].Name < method[j].Name
})
}

for _, method := range constructors {
sort.Slice(method, func(i, j int) bool {
return method[i].Name < method[j].Name
for _, constructor := range info.Constructors {
sort.Slice(constructor, func(i, j int) bool {
return constructor[i].Name < constructor[j].Name
})
}

structNames := make([]string, 0, len(methods))
for _, s := range structs {
structNames = append(structNames, s.Name)
functionNames := make([]string, 0, len(info.Functions))
for functionName := range info.Functions {
functionNames = append(functionNames, functionName)
}
if reorderStructs {
sort.Strings(structNames)
sort.Strings(functionNames)

if opt.ReorderStructs {
info.StructNames.Sort()
}

// Get the source code signature - we will use this to mark the lines to remove later
Expand All @@ -71,37 +85,68 @@ func ReorderSource(filename, formatCommand string, reorderStructs bool, src inte

lineNumberWhereInject := 0
removedLines := 0
for _, typename := range structNames {
for i, sourceCode := range info.Constants {
if removedLines == 0 {
lineNumberWhereInject = structs[typename].OpeningLine
lineNumberWhereInject = info.Constants[i].OpeningLine
}
for ln := sourceCode.OpeningLine - 1; ln < sourceCode.ClosingLine; ln++ {
originalContent[ln] = "// -- " + sign
}
source = append(source, "\n"+sourceCode.SourceCode)
removedLines += len(info.Constants)
}
for i, sourceCode := range info.Variables {
if removedLines == 0 {
lineNumberWhereInject = info.Variables[i].OpeningLine
}
for ln := sourceCode.OpeningLine - 1; ln < sourceCode.ClosingLine; ln++ {
originalContent[ln] = "// -- " + sign
}
source = append(source, "\n"+sourceCode.SourceCode)
removedLines += len(info.Variables)
}
for _, typename := range *info.StructNames {
if removedLines == 0 {
lineNumberWhereInject = info.Structs[typename].OpeningLine
}
// replace the definitions by "// -- line to remove
for ln := structs[typename].OpeningLine - 1; ln < structs[typename].ClosingLine; ln++ {
for ln := info.Structs[typename].OpeningLine - 1; ln < info.Structs[typename].ClosingLine; ln++ {
originalContent[ln] = "// -- " + sign
}
removedLines += structs[typename].ClosingLine - structs[typename].OpeningLine
removedLines += info.Structs[typename].ClosingLine - info.Structs[typename].OpeningLine
// add the struct definition to "source"
source = append(source, "\n\n"+structs[typename].SourceCode)
source = append(source, "\n\n"+info.Structs[typename].SourceCode)

// same for constructors
for _, constructor := range constructors[typename] {
for _, constructor := range info.Constructors[typename] {
for ln := constructor.OpeningLine - 1; ln < constructor.ClosingLine; ln++ {
originalContent[ln] = "// -- " + sign
}
// add the constructor to "source"
source = append(source, "\n"+constructor.SourceCode)
}
removedLines += len(constructors[typename])
removedLines += len(info.Constructors[typename])

// same for methods
for _, method := range methods[typename] {
for _, method := range info.Methods[typename] {
for ln := method.OpeningLine - 1; ln < method.ClosingLine; ln++ {
originalContent[ln] = "// -- " + sign
}
// add the method to "source"
source = append(source, "\n"+method.SourceCode)
}
removedLines += len(methods[typename])
removedLines += len(info.Methods[typename])
}
for _, name := range functionNames {
sourceCode := info.Functions[name]
if removedLines == 0 {
lineNumberWhereInject = info.Functions[name].OpeningLine
}
for ln := sourceCode.OpeningLine - 1; ln < sourceCode.ClosingLine; ln++ {
originalContent[ln] = "// -- " + sign
}
source = append(source, "\n"+sourceCode.SourceCode)
removedLines += len(info.Functions)
}

// add the "source" at the found lineNumberWhereInject
Expand All @@ -118,32 +163,50 @@ func ReorderSource(filename, formatCommand string, reorderStructs bool, src inte
output := strings.Join(originalContent, "\n")

// write in a temporary file and use "gofmt" to format it
newcontent := []byte(output)
switch opt.FormatCommand {
case "gofmt":
// format the temporary file
newcontent, err = format.Source([]byte(output))
if err != nil {
return string(content), errors.New("Failed to format source: " + err.Error())
}
default:
if newcontent, err = formatWithCommand(content, output, opt); err != nil {
return string(content), errors.New("Failed to format source: " + err.Error())
}
}

if opt.Diff {
return doDiff(content, newcontent, opt.Filename)
}
return string(newcontent), nil
}

func formatWithCommand(content []byte, output string, opt ReorderConfig) (newcontent []byte, err error) {
// we use the format command given by the user
// on a temporary file we need to create and remove
tmpfile, err := ioutil.TempFile("", "")
if err != nil {
return string(content), errors.New("Failed to create temp file: " + err.Error())
return content, errors.New("Failed to create temp file: " + err.Error())
}
defer func() {
os.Remove(tmpfile.Name()) // clean up
tmpfile.Close()
}()
defer os.Remove(tmpfile.Name())

// write the temporary file
if _, err := tmpfile.Write([]byte(output)); err != nil {
return string(content), errors.New("Failed to write to temporary file: " + err.Error())
return content, errors.New("Failed to write temp file: " + err.Error())
}
tmpfile.Close()

cmd := exec.Command(formatCommand, "-w", tmpfile.Name())
// format the temporary file
cmd := exec.Command(opt.FormatCommand, "-w", tmpfile.Name())
if err := cmd.Run(); err != nil {
return string(content), err
return content, err
}

// read the temporary file
newcontent, err := ioutil.ReadFile(tmpfile.Name())
newcontent, err = ioutil.ReadFile(tmpfile.Name())
if err != nil {
return string(content), errors.New("Read Temporary File error: " + err.Error())
}

if diff {
return doDiff(content, newcontent, filename)
return content, errors.New("Read Temporary File error: " + err.Error())
}
return string(newcontent), nil
return newcontent, nil
}
Loading

0 comments on commit b90ca79

Please sign in to comment.