Add option to build rosettax87 locally (#9)

Reviewed-on: #9
This commit is contained in:
2025-07-25 22:00:00 +00:00
parent d03c5ee7cc
commit 14db7719a3
10 changed files with 286 additions and 87 deletions

View File

@@ -10,8 +10,8 @@ import (
)
var (
logger zerolog.Logger
wineLogger zerolog.Logger
logger zerolog.Logger
Writer io.Writer
)
func SetupLogging() {
@@ -22,30 +22,14 @@ func SetupLogging() {
return
}
var mw io.Writer
l := createLogWriter(path, 10, 3)
if l != nil {
mw = io.MultiWriter(zerolog.ConsoleWriter{Out: os.Stdout}, l)
Writer = io.MultiWriter(zerolog.ConsoleWriter{Out: os.Stdout}, l)
} else {
mw = io.MultiWriter(zerolog.ConsoleWriter{Out: os.Stdout})
Writer = os.Stdout
}
logger = zerolog.New(mw).With().Timestamp().Logger()
logger = zerolog.New(Writer).With().Timestamp().Logger()
SetLevelInfo()
// Wine logs
wineLogPath, err := getWineLogfilePath()
if err != nil {
Errorf("Failed to get wine log path: %v", err)
return
}
l = createLogWriter(wineLogPath, 25, 1)
if l != nil {
mw = io.MultiWriter(zerolog.ConsoleWriter{Out: os.Stdout}, l)
} else {
mw = io.MultiWriter(zerolog.ConsoleWriter{Out: os.Stdout})
}
wineLogger = zerolog.New(mw).With().Timestamp().Logger()
wineLogger.Level(zerolog.InfoLevel)
}
func createLogWriter(path string, maxSize int, maxBackups int) io.Writer {
@@ -134,9 +118,9 @@ func Panicf(format string, args ...interface{}) {
}
func WineLoggerStdout(msg string) {
wineLogger.Info().Msgf("STDOUT: %s", msg)
logger.Info().Msgf("WINE STDOUT: %s", msg)
}
func WineLoggerStderr(msg string) {
wineLogger.Info().Msgf("STDERR: %s", msg)
logger.Info().Msgf("WINE STDERR: %s", msg)
}

View File

@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"git.burkey.co/eburk/epochcli/pkg/epoch"
"github.com/go-git/go-git/v6"
"io"
"os"
"os/exec"
@@ -84,29 +85,15 @@ func PatchEpoch(myWindow fyne.Window, updateAllStatuses func()) {
log.Debugf("Successfully copied %s to %s", resourceName, destPath)
}
log.Debugf("Preparing rosettax87 directory at: %s", targetRosettaX87Dir)
if err := os.RemoveAll(targetRosettaX87Dir); err != nil {
log.Debugf("Warning: could not remove existing rosettax87 folder '%s': %v", targetRosettaX87Dir, err)
}
if err := os.MkdirAll(targetRosettaX87Dir, 0755); err != nil {
errMsg := fmt.Sprintf("failed to create directory %s: %v", targetRosettaX87Dir, err)
dialog.ShowError(errors.New(errMsg), myWindow)
log.Debug(errMsg)
paths.PatchesAppliedEpoch = false
updateAllStatuses()
return
}
rosettaFilesToCopy := map[string]string{
"rosettax87/rosettax87": filepath.Join(targetRosettaX87Dir, "rosettax87"),
"rosettax87/libRuntimeRosettax87": filepath.Join(targetRosettaX87Dir, "libRuntimeRosettax87"),
}
for resourceName, destPath := range rosettaFilesToCopy {
log.Debugf("Processing rosetta resource: %s to %s", resourceName, destPath)
resource, err := fyne.LoadResourceFromPath(resourceName)
if err != nil {
errMsg := fmt.Sprintf("failed to open bundled resource %s: %v", resourceName, err)
if _, err := os.Stat(filepath.Join(paths.EpochPath, "rosettax87")); err == nil {
log.Debugf("rosettax87 path already exists, skipping copy")
} else {
log.Debugf("Preparing rosettax87 directory at: %s", targetRosettaX87Dir)
if err := os.RemoveAll(targetRosettaX87Dir); err != nil {
log.Debugf("Warning: could not remove existing rosettax87 folder '%s': %v", targetRosettaX87Dir, err)
}
if err := os.MkdirAll(targetRosettaX87Dir, 0755); err != nil {
errMsg := fmt.Sprintf("failed to create directory %s: %v", targetRosettaX87Dir, err)
dialog.ShowError(errors.New(errMsg), myWindow)
log.Debug(errMsg)
paths.PatchesAppliedEpoch = false
@@ -114,40 +101,58 @@ func PatchEpoch(myWindow fyne.Window, updateAllStatuses func()) {
return
}
destinationFile, err := os.Create(destPath)
if err != nil {
errMsg := fmt.Sprintf("failed to create destination file %s: %v", destPath, err)
dialog.ShowError(errors.New(errMsg), myWindow)
log.Debug(errMsg)
paths.PatchesAppliedEpoch = false
updateAllStatuses()
return
rosettaFilesToCopy := map[string]string{
"rosettax87/rosettax87": filepath.Join(targetRosettaX87Dir, "rosettax87"),
"rosettax87/libRuntimeRosettax87": filepath.Join(targetRosettaX87Dir, "libRuntimeRosettax87"),
}
_, err = io.Copy(destinationFile, bytes.NewReader(resource.Content()))
if err != nil {
destinationFile.Close()
errMsg := fmt.Sprintf("failed to copy bundled resource %s to %s: %v", resourceName, destPath, err)
dialog.ShowError(errors.New(errMsg), myWindow)
log.Debug(errMsg)
paths.PatchesAppliedEpoch = false
updateAllStatuses()
return
}
destinationFile.Close()
if filepath.Base(destPath) == "rosettax87" {
log.Debugf("Setting execute permission for %s", destPath)
if err := os.Chmod(destPath, 0755); err != nil {
errMsg := fmt.Sprintf("failed to set execute permission for %s: %v", destPath, err)
for resourceName, destPath := range rosettaFilesToCopy {
log.Debugf("Processing rosetta resource: %s to %s", resourceName, destPath)
resource, err := fyne.LoadResourceFromPath(resourceName)
if err != nil {
errMsg := fmt.Sprintf("failed to open bundled resource %s: %v", resourceName, err)
dialog.ShowError(errors.New(errMsg), myWindow)
log.Debug(errMsg)
paths.PatchesAppliedEpoch = false
updateAllStatuses()
return
}
destinationFile, err := os.Create(destPath)
if err != nil {
errMsg := fmt.Sprintf("failed to create destination file %s: %v", destPath, err)
dialog.ShowError(errors.New(errMsg), myWindow)
log.Debug(errMsg)
paths.PatchesAppliedEpoch = false
updateAllStatuses()
return
}
_, err = io.Copy(destinationFile, bytes.NewReader(resource.Content()))
if err != nil {
destinationFile.Close()
errMsg := fmt.Sprintf("failed to copy bundled resource %s to %s: %v", resourceName, destPath, err)
dialog.ShowError(errors.New(errMsg), myWindow)
log.Debug(errMsg)
paths.PatchesAppliedEpoch = false
updateAllStatuses()
return
}
destinationFile.Close()
if filepath.Base(destPath) == "rosettax87" {
log.Debugf("Setting execute permission for %s", destPath)
if err := os.Chmod(destPath, 0755); err != nil {
errMsg := fmt.Sprintf("failed to set execute permission for %s: %v", destPath, err)
dialog.ShowError(errors.New(errMsg), myWindow)
log.Debug(errMsg)
paths.PatchesAppliedEpoch = false
updateAllStatuses()
return
}
}
log.Debugf("Successfully copied %s to %s", resourceName, destPath)
}
log.Debugf("Successfully copied %s to %s", resourceName, destPath)
}
log.Debugf("Checking dlls.txt file at: %s", dllsTextFile)
@@ -266,7 +271,7 @@ func PatchCrossOver(myWindow fyne.Window, updateAllStatuses func()) {
if strings.Contains(err.Error(), "operation not permitted") {
errMsg += "\n\nSolution: Open System Settings, go to Privacy & Security > App Management, and enable EpochSilicon."
}
dialog.ShowError(fmt.Errorf(errMsg), myWindow)
dialog.ShowError(fmt.Errorf("%s", errMsg), myWindow)
paths.PatchesAppliedCrossOver = false
updateAllStatuses()
return
@@ -462,3 +467,42 @@ func isConfigSettingCorrect(configText, setting, expectedValue string) bool {
currentValue := matches[1]
return currentValue == expectedValue
}
func BuildRosetta() (string, string, error) {
tmpDir, err := os.MkdirTemp("", "rosettax87")
if err != nil {
return "", "", fmt.Errorf("failed to create temporary directory: %v", err)
}
clonedDir := filepath.Join(tmpDir, "rosettax87")
_, err = git.PlainClone(clonedDir, &git.CloneOptions{
URL: "https://github.com/fputs/rosettax87",
Progress: os.Stdout,
RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
SingleBranch: true,
})
if err != nil {
return "", "", fmt.Errorf("failed to clone repository: %v", err)
}
cmd := exec.Command("cmake", "-B", "build")
cmd.Dir = clonedDir
err = cmd.Run()
if err != nil {
return "", "", fmt.Errorf("failed to create build files: %v", err)
}
cmd = exec.Command("cmake", "--build", "build")
cmd.Dir = clonedDir
err = cmd.Run()
if err != nil {
return "", "", fmt.Errorf("failed to build rosettax87: %v", err)
}
buildDir := filepath.Join(clonedDir, "build")
rosettax87Path := filepath.Join(buildDir, "rosettax87")
librosettaPath := filepath.Join(buildDir, "libRuntimeRosettax87")
return rosettax87Path, librosettaPath, nil
}

View File

@@ -0,0 +1,15 @@
package patching
import (
"fmt"
"testing"
)
func TestBuildRosetta(t *testing.T) {
rp, lp, err := BuildRosetta()
if err != nil {
t.Error(err)
}
fmt.Println("exe path:", rp)
fmt.Println("lib path:", lp)
}

View File

@@ -137,7 +137,7 @@ func createBottomBar(myWindow fyne.Window) fyne.CanvasObject {
})
// Git button
gitButton := widget.NewButton("Source Code", func() {
gitButton := widget.NewButton("Website", func() {
githubURL := "https://git.burkey.co/eburk/epochsilicon"
parsedURL, err := url.Parse(githubURL)
if err != nil {

View File

@@ -1,15 +1,18 @@
package ui
import (
"epochsilicon/pkg/log"
"epochsilicon/pkg/patching"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
"os"
"os/exec"
"path/filepath"
"strings"
"howett.net/plist"
@@ -198,12 +201,84 @@ func showTroubleshootingPopup() {
}, currentWindow).Show()
})
// --- Build Rosettax87 Locally ---
buildRosettaButton = widget.NewButton("Build", func() {
msg := "Building rosettax87 on your computer may speed up launch times. This requires xcode-commandline-tools and Cmake to be installed. See the instructions at https://git.burkey.co/eburk/epochsilicon/README.md\n\n"
msg += "Click YES to start the build process. This could take up to a minute depending on the the speed of your Mac. A popup will let you know when the files have been built and copied to the right place."
dialog.NewConfirm("Build rosettax87", msg, func(confirm bool) {
if confirm {
// Check for dependencies
if _, err := exec.LookPath("clang"); err != nil {
m := fmt.Errorf("xcode command line tools are not installed on your computer. Click the Website button in the app and read the instructions on building rosettax87 before trying again")
log.Error(m.Error())
dialog.ShowError(m, currentWindow)
return
}
if _, err := exec.LookPath("cmake"); err != nil {
m := fmt.Errorf("Cmake is not installed on your computer. Click the Website button in the app and read the instructions on building rosettax87 before trying again")
log.Error(m.Error())
dialog.ShowError(m, currentWindow)
return
}
xPath, lPath, err := patching.BuildRosetta()
if err != nil {
m := fmt.Errorf("Error building rosettax87: %v\nClick Website for information on getting help", err)
log.Error(m.Error())
dialog.ShowError(m, currentWindow)
return
}
d := filepath.Join(paths.EpochPath, "rosettax87")
if err = os.RemoveAll(d); err != nil {
m := fmt.Errorf("Error removing existing rosettax87 directory: %v", err)
log.Error(m.Error())
dialog.ShowError(m, currentWindow)
return
}
if err = os.MkdirAll(d, 0755); err != nil {
m := fmt.Errorf("Error creating existing rosettax87 directory: %v", err)
log.Error(m.Error())
dialog.ShowError(m, currentWindow)
return
}
pathMap := map[string]string{
xPath: filepath.Join(paths.EpochPath, "rosettax87", "rosettax87"),
lPath: filepath.Join(paths.EpochPath, "rosettax87", "libRuntimeRosettax87"),
}
for srcPath, destPath := range pathMap {
fBytes, err := os.ReadFile(srcPath)
if err != nil {
errMsg := fmt.Sprintf("failed to read source file %s: %v", srcPath, err)
dialog.ShowError(errors.New(errMsg), currentWindow)
log.Debug(errMsg)
return
}
err = os.WriteFile(destPath, fBytes, 0755)
if err != nil {
errMsg := fmt.Sprintf("failed to write file %s: %v", destPath, err)
dialog.ShowError(errors.New(errMsg), currentWindow)
log.Debug(errMsg)
return
}
log.Debugf("Successfully copied %s to %s", srcPath, destPath)
}
dialog.ShowInformation("Build Successful", "Rosettax87 installed successfully.", currentWindow)
}
}, currentWindow).Show()
})
troubleshootingTitle := widget.NewLabel("Troubleshooting")
troubleshootingTitle.TextStyle = fyne.TextStyle{Bold: true}
rowCrossover := container.NewBorder(nil, nil, widget.NewLabel("CrossOver version:"), crossoverStatusShort, nil)
rowWDB := container.NewBorder(nil, nil, widget.NewLabel("Delete WDB directory (cache):"), wdbDeleteButton, nil)
rowWine := container.NewBorder(nil, nil, widget.NewLabel("Delete Wine prefixes (~/.wine & Epoch/.wine):"), wineDeleteButton, nil)
rowBuildRosetta := container.NewBorder(nil, nil, widget.NewLabel("Build Rosettax87 locally:"), buildRosettaButton, nil)
appMgmtNote := widget.NewLabel("Please ensure EpochSilicon is enabled in System Settings > Privacy & Security > App Management.")
appMgmtNote.Wrapping = fyne.TextWrapWord
appMgmtNote.TextStyle = fyne.TextStyle{Italic: true}
@@ -215,6 +290,7 @@ func showTroubleshootingPopup() {
crossoverStatusDetail,
rowWDB,
rowWine,
rowBuildRosetta,
appMgmtNote,
)
@@ -223,11 +299,11 @@ func showTroubleshootingPopup() {
troubleshootingCloseButton = widget.NewButton("Close", func() {})
popupContent := container.NewBorder(
nil, // top
nil, // top
container.NewCenter(troubleshootingCloseButton), // bottom
nil, // left
nil, // right
container.NewPadded(scrollContainer), // center
nil, // left
nil, // right
container.NewPadded(scrollContainer), // center
)
windowSize := currentWindow.Content().Size()

View File

@@ -61,6 +61,7 @@ var (
crossoverVersionStatusLabel *widget.RichText
wdbDeleteButton *widget.Button
wineDeleteButton *widget.Button
buildRosettaButton *widget.Button
appMgmtPermissionButton *widget.Button
troubleshootingCloseButton *widget.Button
)