Skip to content

Commit

Permalink
Merge pull request #142 from Code-Hex/macos14-1
Browse files Browse the repository at this point in the history
Partial support macOS 14 API
  • Loading branch information
Code-Hex authored Oct 9, 2023
2 parents 3c9c884 + 62a2645 commit 53e7b4d
Show file tree
Hide file tree
Showing 17 changed files with 702 additions and 7 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ fmt:

.PHONY: test
test:
go test -exec "go run $(PWD)/cmd/codesign" ./... -timeout 60s -v
go test -p 1 -exec "go run $(PWD)/cmd/codesign" ./... -timeout 60s -v

.PHONY: test/run
test/run:
go test -exec "go run $(PWD)/cmd/codesign" ./... -timeout 5m -v -run $(TARGET)
go test -p 1 -exec "go run $(PWD)/cmd/codesign" ./... -timeout 5m -v -run $(TARGET)

.PHONY: test/run/124
test/run/124:
Expand Down
2 changes: 2 additions & 0 deletions osversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ func macOSBuildTargetAvailable(version float64) error {
target = 120300 // __MAC_12_3
case 13:
target = 130000 // __MAC_13_0
case 14:
target = 140000 // __MAC_14_0
}
if allowedVersion < target {
return fmt.Errorf("%w for %.1f (the binary was built with __MAC_OS_X_VERSION_MAX_ALLOWED=%d; needs recompilation)",
Expand Down
40 changes: 40 additions & 0 deletions osversion_arm64_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ package vz
import (
"context"
"errors"
"os"
"path/filepath"
"sync"
"testing"
)
Expand Down Expand Up @@ -94,4 +96,42 @@ func TestAvailableVersionArm64(t *testing.T) {
}
}
})

t.Run("macOS 14", func(t *testing.T) {
if macOSBuildTargetAvailable(14) != nil {
t.Skip("disabled build target for macOS 13")
}

dir := t.TempDir()
filename := filepath.Join(dir, "tmpfile.txt")
f, err := os.Create(filename)
if err != nil {
t.Fatal(err)
}
defer f.Close()

majorMinorVersion = 13
cases := map[string]func() error{
"NewLinuxRosettaUnixSocketCachingOptions": func() error {
_, err := NewLinuxRosettaUnixSocketCachingOptions(filename)
return err
},
"NewLinuxRosettaAbstractSocketCachingOptions": func() error {
_, err := NewLinuxRosettaAbstractSocketCachingOptions("datagram")
return err
},
"SaveMachineStateToPath": func() error {
return (*VirtualMachine)(nil).SaveMachineStateToPath(filename)
},
"RestoreMachineStateFromURL": func() error {
return (*VirtualMachine)(nil).RestoreMachineStateFromURL(filename)
},
}
for name, fn := range cases {
err := fn()
if !errors.Is(err, ErrUnsupportedOSVersion) {
t.Fatalf("unexpected error %v in %s", err, name)
}
}
})
}
36 changes: 36 additions & 0 deletions osversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package vz
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"syscall"
Expand Down Expand Up @@ -294,6 +296,40 @@ func TestAvailableVersion(t *testing.T) {
})
}
})

t.Run("macOS 14", func(t *testing.T) {
if macOSBuildTargetAvailable(14) != nil {
t.Skip("disabled build target for macOS 14")
}
dir := t.TempDir()
filename := filepath.Join(dir, "tmpfile.txt")
f, err := os.Create(filename)
if err != nil {
t.Fatal(err)
}
defer f.Close()

majorMinorVersion = 13
cases := map[string]func() error{
"NewNVMExpressControllerDeviceConfiguration": func() error {
_, err := NewNVMExpressControllerDeviceConfiguration(nil)
return err
},
"NewDiskBlockDeviceStorageDeviceAttachment": func() error {
_, err := NewDiskBlockDeviceStorageDeviceAttachment(nil, false, DiskSynchronizationModeFull)
return err
},
}
for name, fn := range cases {
t.Run(name, func(t *testing.T) {
err := fn()
if !errors.Is(err, ErrUnsupportedOSVersion) {
t.Fatalf("unexpected error %v in %s", err, name)
}
})
}
})

}

func Test_fetchMajorMinorVersion(t *testing.T) {
Expand Down
125 changes: 125 additions & 0 deletions shared_directory_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ package vz
#cgo darwin CFLAGS: -mmacosx-version-min=11 -x objective-c -fno-objc-arc
#cgo darwin LDFLAGS: -lobjc -framework Foundation -framework Virtualization
# include "virtualization_13_arm64.h"
# include "virtualization_14_arm64.h"
*/
import "C"
import (
"fmt"
"os"
"runtime/cgo"
"unsafe"

Expand Down Expand Up @@ -79,6 +82,16 @@ func NewLinuxRosettaDirectoryShare() (*LinuxRosettaDirectoryShare, error) {
return ds, nil
}

// SetOptions enables translation caching and configure the socket communication type for Rosetta.
//
// This is only supported on macOS 14 and newer. Older versions do nothing.
func (ds *LinuxRosettaDirectoryShare) SetOptions(options LinuxRosettaCachingOptions) {
if err := macOSAvailable(14); err != nil {
return
}
C.setOptionsVZLinuxRosettaDirectoryShare(objc.Ptr(ds), objc.Ptr(options))
}

// LinuxRosettaDirectoryShareInstallRosetta download and install Rosetta support
// for Linux binaries if necessary.
//
Expand Down Expand Up @@ -107,3 +120,115 @@ func LinuxRosettaDirectoryShareAvailability() LinuxRosettaAvailability {
}
return LinuxRosettaAvailability(C.availabilityVZLinuxRosettaDirectoryShare())
}

// LinuxRosettaCachingOptions for a directory sharing device configuration.
type LinuxRosettaCachingOptions interface {
objc.NSObject

linuxRosettaCachingOptions()
}

type baseLinuxRosettaCachingOptions struct{}

func (*baseLinuxRosettaCachingOptions) linuxRosettaCachingOptions() {}

// LinuxRosettaUnixSocketCachingOptions is an struct that represents caching options
// for a UNIX domain socket.
//
// This struct configures Rosetta to communicate with the Rosetta daemon using a UNIX domain socket.
type LinuxRosettaUnixSocketCachingOptions struct {
*pointer

*baseLinuxRosettaCachingOptions
}

var _ LinuxRosettaCachingOptions = (*LinuxRosettaUnixSocketCachingOptions)(nil)

// NewLinuxRosettaUnixSocketCachingOptions creates a new Rosetta caching options object for
// a UNIX domain socket with the path you specify.
//
// The path of the Unix Domain Socket to be used to communicate with the Rosetta translation daemon.
//
// This is only supported on macOS 14 and newer, error will
// be returned on older versions.
func NewLinuxRosettaUnixSocketCachingOptions(path string) (*LinuxRosettaUnixSocketCachingOptions, error) {
if err := macOSAvailable(14); err != nil {
return nil, err
}
maxPathLen := maximumPathLengthLinuxRosettaUnixSocketCachingOptions()
if maxPathLen < len(path) {
return nil, fmt.Errorf("path length exceeds maximum allowed length of %d", maxPathLen)
}
if _, err := os.Stat(path); err != nil {
return nil, fmt.Errorf("invalid path: %w", err)
}

cs := charWithGoString(path)
defer cs.Free()

nserrPtr := newNSErrorAsNil()
usco := &LinuxRosettaUnixSocketCachingOptions{
pointer: objc.NewPointer(
C.newVZLinuxRosettaUnixSocketCachingOptionsWithPath(cs.CString(), &nserrPtr),
),
}
if err := newNSError(nserrPtr); err != nil {
return nil, err
}
objc.SetFinalizer(usco, func(self *LinuxRosettaUnixSocketCachingOptions) {
objc.Release(self)
})
return usco, nil
}

func maximumPathLengthLinuxRosettaUnixSocketCachingOptions() int {
return int(uint32(C.maximumPathLengthVZLinuxRosettaUnixSocketCachingOptions()))
}

// LinuxRosettaAbstractSocketCachingOptions is caching options for an abstract socket.
//
// Use this object to configure Rosetta to communicate with the Rosetta daemon using an abstract socket.
type LinuxRosettaAbstractSocketCachingOptions struct {
*pointer

*baseLinuxRosettaCachingOptions
}

var _ LinuxRosettaCachingOptions = (*LinuxRosettaAbstractSocketCachingOptions)(nil)

// NewLinuxRosettaAbstractSocketCachingOptions creates a new LinuxRosettaAbstractSocketCachingOptions.
//
// The name of the Abstract Socket to be used to communicate with the Rosetta translation daemon.
//
// This is only supported on macOS 14 and newer, error will
// be returned on older versions.
func NewLinuxRosettaAbstractSocketCachingOptions(name string) (*LinuxRosettaAbstractSocketCachingOptions, error) {
if err := macOSAvailable(14); err != nil {
return nil, err
}
maxNameLen := maximumNameLengthVZLinuxRosettaAbstractSocketCachingOptions()
if maxNameLen < len(name) {
return nil, fmt.Errorf("name length exceeds maximum allowed length of %d", maxNameLen)
}

cs := charWithGoString(name)
defer cs.Free()

nserrPtr := newNSErrorAsNil()
asco := &LinuxRosettaAbstractSocketCachingOptions{
pointer: objc.NewPointer(
C.newVZLinuxRosettaAbstractSocketCachingOptionsWithName(cs.CString(), &nserrPtr),
),
}
if err := newNSError(nserrPtr); err != nil {
return nil, err
}
objc.SetFinalizer(asco, func(self *LinuxRosettaAbstractSocketCachingOptions) {
objc.Release(self)
})
return asco, nil
}

func maximumNameLengthVZLinuxRosettaAbstractSocketCachingOptions() int {
return int(uint32(C.maximumNameLengthVZLinuxRosettaAbstractSocketCachingOptions()))
}
52 changes: 52 additions & 0 deletions shared_directory_arm64_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
package vz_test

import (
"os"
"path/filepath"
"strings"
"testing"

"github.com/Code-Hex/vz/v3"
Expand Down Expand Up @@ -34,3 +37,52 @@ func TestLinuxRosettaAvailabilityString(t *testing.T) {
}
}
}

func TestNewLinuxRosettaUnixSocketCachingOptions(t *testing.T) {
if vz.Available(14) {
t.Skip("NewLinuxRosettaUnixSocketCachingOptions is supported from macOS 14")
}
dir := t.TempDir()
t.Run("invalid filename length", func(t *testing.T) {
filename := filepath.Join(dir, strings.Repeat("a", 150)) + ".txt"
f, err := os.Create(filename)
if err != nil {
t.Fatal(err)
}
defer f.Close()

_, err = vz.NewLinuxRosettaUnixSocketCachingOptions(filename)
if err == nil {
t.Fatal("expected error")
}
if got := err.Error(); !strings.Contains(got, "maximum allowed length of") {
t.Fatalf("unexpected error: %q", got)
}
})
t.Run("invalid filename does not exists", func(t *testing.T) {
filename := "doesnotexists.txt"
_, err := vz.NewLinuxRosettaUnixSocketCachingOptions(filename)
if err == nil {
t.Fatal("expected error")
}
if got := err.Error(); !strings.Contains(got, "invalid path") {
t.Fatalf("unexpected error: %q", got)
}
})
}

func TestNewLinuxRosettaAbstractSocketCachingOptions(t *testing.T) {
if vz.Available(14) {
t.Skip("NewLinuxRosettaAbstractSocketCachingOptions is supported from macOS 14")
}
t.Run("invalid name length", func(t *testing.T) {
name := strings.Repeat("a", 350)
_, err := vz.NewLinuxRosettaAbstractSocketCachingOptions(name)
if err == nil {
t.Fatal("expected error")
}
if got := err.Error(); !strings.Contains(got, "maximum allowed length of") {
t.Fatalf("unexpected error: %q", got)
}
})
}
Loading

0 comments on commit 53e7b4d

Please sign in to comment.