deviceinfo: replace implementation with mvdan/sh (MR 52)
This library has a convenient "source file" method designed for sourcing shell envs and returning values set in them. deviceinfo's syntax every where else seems to be "whatever sh can 'source'", so using this library seems a lot nicer than trying to implement a parser/interpreter here (and almost certainly missing corner cases, functionality, etc) [ci:skip-build]: already built successfully in CI
This commit is contained in:
10
go.mod
10
go.mod
@@ -7,5 +7,13 @@ require (
|
||||
github.com/klauspost/compress v1.15.12
|
||||
github.com/pierrec/lz4/v4 v4.1.17
|
||||
github.com/ulikunitz/xz v0.5.10
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||
golang.org/x/sys v0.18.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/mvdan/sh v2.6.4+incompatible // indirect
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/term v0.18.0 // indirect
|
||||
mvdan.cc/sh v2.6.4+incompatible // indirect
|
||||
)
|
||||
|
12
go.sum
12
go.sum
@@ -2,9 +2,21 @@ github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e h1:hHg27A0RS
|
||||
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
|
||||
github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
|
||||
github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/mvdan/sh v2.6.4+incompatible h1:D4oEWW0J8cL7zeQkrXw76IAYXF0mJfDaBwjgzmKb6zs=
|
||||
github.com/mvdan/sh v2.6.4+incompatible/go.mod h1:kipHzrJQZEDCMTNRVRAlMMFjqHEYrthfIlFkJSrmDZE=
|
||||
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
|
||||
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
|
||||
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
mvdan.cc/sh v2.6.4+incompatible h1:eD6tDeh0pw+/TOTI1BBEryZ02rD2nMcFsgcvde7jffM=
|
||||
mvdan.cc/sh v2.6.4+incompatible/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8=
|
||||
|
@@ -4,14 +4,14 @@
|
||||
package deviceinfo
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mvdan/sh/shell"
|
||||
"gitlab.com/postmarketOS/postmarketos-mkinitfs/internal/misc"
|
||||
)
|
||||
|
||||
@@ -20,6 +20,7 @@ type DeviceInfo struct {
|
||||
InitfsExtraCompression string
|
||||
UbootBoardname string
|
||||
GenerateSystemdBoot string
|
||||
FormatVersion string
|
||||
}
|
||||
|
||||
// Reads the relevant entries from "file" into DeviceInfo struct
|
||||
@@ -32,13 +33,7 @@ func (d *DeviceInfo) ReadDeviceinfo(file string) error {
|
||||
return fmt.Errorf("unexpected error getting status for %q: %s", file, err)
|
||||
}
|
||||
|
||||
fd, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
if err := d.unmarshal(fd); err != nil {
|
||||
if err := d.unmarshal(file); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -46,53 +41,44 @@ func (d *DeviceInfo) ReadDeviceinfo(file string) error {
|
||||
}
|
||||
|
||||
// Unmarshals a deviceinfo into a DeviceInfo struct
|
||||
func (d *DeviceInfo) unmarshal(r io.Reader) error {
|
||||
s := bufio.NewScanner(r)
|
||||
for s.Scan() {
|
||||
line := s.Text()
|
||||
if strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
// line isn't setting anything, so just ignore it
|
||||
if !strings.Contains(line, "=") {
|
||||
continue
|
||||
}
|
||||
|
||||
// sometimes line has a comment at the end after setting an option
|
||||
line = strings.SplitN(line, "#", 2)[0]
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
// must support having '=' in the value (e.g. kernel cmdline)
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("error parsing deviceinfo line, invalid format: %s", line)
|
||||
}
|
||||
|
||||
name, val := parts[0], parts[1]
|
||||
val = strings.ReplaceAll(val, "\"", "")
|
||||
|
||||
if name == "deviceinfo_format_version" && val != "0" {
|
||||
return fmt.Errorf("deviceinfo format version %q is not supported", val)
|
||||
}
|
||||
|
||||
fieldName := nameToField(name)
|
||||
|
||||
if fieldName == "" {
|
||||
return fmt.Errorf("error parsing deviceinfo line, invalid format: %s", line)
|
||||
}
|
||||
func (d *DeviceInfo) unmarshal(file string) error {
|
||||
ctx, cancelCtx := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
|
||||
defer cancelCtx()
|
||||
vars, err := shell.SourceFile(ctx, file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing deviceinfo %q failed: %w", file, err)
|
||||
}
|
||||
|
||||
for k, v := range vars {
|
||||
fieldName := nameToField(k)
|
||||
field := reflect.ValueOf(d).Elem().FieldByName(fieldName)
|
||||
if !field.IsValid() {
|
||||
// an option that meets the deviceinfo "specification", but isn't
|
||||
// one we care about in this module
|
||||
continue
|
||||
}
|
||||
field.SetString(val)
|
||||
switch field.Interface().(type) {
|
||||
case string:
|
||||
field.SetString(v.String())
|
||||
case bool:
|
||||
if v, err := strconv.ParseBool(v.String()); err != nil {
|
||||
return fmt.Errorf("deviceinfo %q has unsupported type for field %q, expected 'bool'", file, k)
|
||||
} else {
|
||||
field.SetBool(v)
|
||||
}
|
||||
case int:
|
||||
if v, err := strconv.ParseInt(v.String(), 10, 32); err != nil {
|
||||
return fmt.Errorf("deviceinfo %q has unsupported type for field %q, expected 'int'", file, k)
|
||||
} else {
|
||||
field.SetInt(v)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("deviceinfo %q has unsupported type for field %q", file, k)
|
||||
}
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
log.Print("unable to parse deviceinfo: ", err)
|
||||
return err
|
||||
|
||||
if d.FormatVersion != "0" {
|
||||
return fmt.Errorf("deviceinfo %q has an unsupported format version %q", file, d.FormatVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -125,8 +111,10 @@ func (d DeviceInfo) String() string {
|
||||
%s: %v
|
||||
%s: %v
|
||||
%s: %v
|
||||
%s: %v
|
||||
}`,
|
||||
"deviceinfo_format_version", d.FormatVersion,
|
||||
"deviceinfo_", d.FormatVersion,
|
||||
"deviceinfo_initfs_compression", d.InitfsCompression,
|
||||
"deviceinfo_initfs_extra_compression", d.InitfsCompression,
|
||||
"deviceinfo_ubootBoardname", d.UbootBoardname,
|
||||
|
@@ -4,8 +4,6 @@
|
||||
package deviceinfo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -59,37 +57,24 @@ func TestUnmarshal(t *testing.T) {
|
||||
tables := []struct {
|
||||
// field is just used for reflection within the test, so it must be a
|
||||
// valid DeviceInfo field
|
||||
field string
|
||||
in string
|
||||
expected string
|
||||
file string
|
||||
expected DeviceInfo
|
||||
}{
|
||||
{"InitfsCompression", "deviceinfo_initfs_compression=\"gzip:-9\"\n", "gzip:-9"},
|
||||
// line with multiple '='
|
||||
{"InitfsCompression", "deviceinfo_initfs_compression=zstd:--foo=1 -T0 --bar=bazz", "zstd:--foo=1 -T0 --bar=bazz"},
|
||||
// empty option
|
||||
{"InitfsCompression", "deviceinfo_initfs_compression=\"\"\n", ""},
|
||||
// line with comment at the end
|
||||
{"", "# this is a comment!\n", ""},
|
||||
// empty lines are fine
|
||||
{"", "", ""},
|
||||
// line with whitepace characters only
|
||||
{"", " \t \n\r", ""},
|
||||
{"./test_resources/deviceinfo-unmarshal-1", DeviceInfo{
|
||||
FormatVersion: "0",
|
||||
UbootBoardname: "foobar-bazz",
|
||||
InitfsCompression: "zstd:--foo=1 -T0 --bar=bazz",
|
||||
InitfsExtraCompression: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
var d DeviceInfo
|
||||
for _, table := range tables {
|
||||
testName := fmt.Sprintf("unmarshal::'%s':", strings.ReplaceAll(table.in, "\n", "\\n"))
|
||||
if err := d.unmarshal(strings.NewReader(table.in)); err != nil {
|
||||
t.Errorf("%s received an unexpected err: ", err)
|
||||
if err := d.unmarshal(table.file); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Check against expected value
|
||||
field := reflect.ValueOf(&d).Elem().FieldByName(table.field)
|
||||
out := ""
|
||||
if table.field != "" {
|
||||
out = field.String()
|
||||
}
|
||||
if out != table.expected {
|
||||
t.Errorf("%s expected: %q, got: %q", testName, table.expected, out)
|
||||
if d != table.expected {
|
||||
t.Errorf("expected: %s, got: %s", table.expected, d)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,2 +1,3 @@
|
||||
deviceinfo_format_version="0"
|
||||
deviceinfo_initfs_compression="gz -9"
|
||||
deviceinfo_mesa_driver="panfrost"
|
||||
|
@@ -1 +1,2 @@
|
||||
deviceinfo_mesa_driver="msm"
|
||||
deviceinfo_format_version="0"
|
||||
deviceinfo_mesa_driver="msm"
|
||||
|
6
pkgs/deviceinfo/test_resources/deviceinfo-unmarshal-1
Normal file
6
pkgs/deviceinfo/test_resources/deviceinfo-unmarshal-1
Normal file
@@ -0,0 +1,6 @@
|
||||
deviceinfo_format_version="0"
|
||||
deviceinfo_uboot_boardname="foobar-bazz"
|
||||
# line with multiple =
|
||||
deviceinfo_initfs_compression="zstd:--foo=1 -T0 --bar=bazz"
|
||||
# empty option
|
||||
deviceinfo_initfs_extra_compression=""
|
Reference in New Issue
Block a user