summaryrefslogtreecommitdiff
path: root/Magefile.go
blob: 49a5fb449583b80b78907ec4c71cd77d27eedeb8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//go:build mage
// +build mage

package main

import (
    "io"
    "fmt"
    "os"
    "path/filepath"
    "runtime"
    "strconv"
    "time"

    "github.com/magefile/mage/sh"
)

// Build compiles the quicklogger app into ./bin.
func Build() error {
    outDir := "bin"
    if err := os.MkdirAll(outDir, 0o755); err != nil {
        return err
    }

    binName := "quicklogger"
    if runtime.GOOS == "windows" {
        binName += ".exe"
    }

    out := filepath.Join(outDir, binName)
    fmt.Printf("Building %s\n", out)
    return sh.RunV("go", "build", "-o", out, ".")
}

// Run launches the app with `go run .`.
func Run() error {
    fmt.Println("Running quicklogger (verbose build)")
    // Show Go version and key env for diagnostics
    _ = sh.RunV("go", "version")
    _ = sh.RunV("go", "env", "GOVERSION", "GOOS", "GOARCH", "GOMOD", "GOMODCACHE", "GOCACHE")
    // Compile+run with verbose and trace flags to show build steps
    return sh.RunV("go", "run", "-v", "-x", ".")
}

// Clean removes build artifacts (bin/ and local APK).
func Clean() error {
    fmt.Println("Cleaning build artifacts")
    // Remove bin directory
    if err := os.RemoveAll("bin"); err != nil {
        return err
    }
    // Remove generated APK if present
    _ = os.Remove("quicklogger.apk")
    return nil
}

// Android packages an Android APK via Fyne and copies it to ~/Documents/APKs if present.
func Android() error {
    env := map[string]string{}

    // Respect existing ANDROID_NDK_HOME. If not set, try legacy var or a common default path.
    ndk := os.Getenv("ANDROID_NDK_HOME")
    if ndk == "" {
        // Some setups export a misspelled var; honor it if present.
        ndk = os.Getenv("ANDORID_NDK_HOME")
    }
    if ndk == "" {
        if home, err := os.UserHomeDir(); err == nil {
            guess := filepath.Join(home, "android-ndk", "android-ndk-r26b")
            env["ANDROID_NDK_HOME"] = guess
            fmt.Printf("ANDROID_NDK_HOME not set, guessing %s\n", guess)
        }
    }

    fmt.Println("Packaging Android APK via Fyne")
    if err := sh.RunWithV(env, "fyne", "package", "-os", "android"); err != nil {
        return err
    }

    // If ~/Documents/APKs exists, copy the APK there.
    home, err := os.UserHomeDir()
    if err != nil {
        return nil // packaging succeeded; copying is best-effort
    }
    destDir := filepath.Join(home, "Documents", "APKs")
    if st, err := os.Stat(destDir); err == nil && st.IsDir() {
        src := "quicklogger.apk"
        if _, err := os.Stat(src); err == nil {
            dst := filepath.Join(destDir, filepath.Base(src))
            if err := copyFile(src, dst); err != nil {
                return err
            }
            fmt.Printf("Copied %s to %s\n", src, dst)
        }
    }
    return nil
}

// AndroidCross builds an Android APK using fyne-cross with Docker/Podman.
// Mirrors steps from README using a Podman socket if available.
func AndroidCross() error {
    env := map[string]string{}

    // If DOCKER_HOST is not set, try a sensible Podman socket default.
    if os.Getenv("DOCKER_HOST") == "" {
        uid := os.Geteuid()
        sock := filepath.Join("/run/user", strconv.Itoa(uid), "podman", "podman.sock")
        if _, err := os.Stat(sock); err == nil {
            env["DOCKER_HOST"] = "unix://" + sock
            fmt.Printf("Using Podman socket: %s\n", env["DOCKER_HOST"])
        } else {
            fmt.Println("DOCKER_HOST is not set and no Podman socket found; relying on default Docker daemon.")
        }
    }

    // Ensure fyne-cross is available; attempt install if not.
    if _, err := sh.Output("which", "fyne-cross"); err != nil {
        fmt.Println("Installing fyne-cross (requires network access)...")
        if err := sh.RunV("go", "install", "github.com/fyne-io/fyne-cross@latest"); err != nil {
            return fmt.Errorf("fyne-cross not found and installation failed: %w", err)
        }
    }

    // Pull/update builder image then build.
    if err := sh.RunWithV(env, "fyne-cross", "android", "--pull"); err != nil {
        return err
    }
    if err := sh.RunWithV(env, "fyne-cross", "android"); err != nil {
        return err
    }

    // Copy newest APK to ~/Documents/APKs if available.
    apk, err := newestAPK("fyne-cross/dist/android")
    if err != nil {
        // Not fatal; print a note and finish
        fmt.Printf("Note: could not locate built APK: %v\n", err)
        return nil
    }
    home, herr := os.UserHomeDir()
    if herr != nil {
        return nil
    }
    destDir := filepath.Join(home, "Documents", "APKs")
    if st, err := os.Stat(destDir); err == nil && st.IsDir() {
        dst := filepath.Join(destDir, filepath.Base(apk))
        if err := copyFile(apk, dst); err != nil {
            return err
        }
        fmt.Printf("Copied %s to %s\n", apk, dst)
    }
    return nil
}

func newestAPK(dir string) (string, error) {
    entries, err := os.ReadDir(dir)
    if err != nil {
        return "", err
    }
    var newest string
    var newestMod time.Time
    for _, e := range entries {
        if e.IsDir() {
            continue
        }
        name := e.Name()
        if filepath.Ext(name) != ".apk" {
            continue
        }
        info, err := e.Info()
        if err != nil {
            continue
        }
        if info.ModTime().After(newestMod) {
            newestMod = info.ModTime()
            newest = filepath.Join(dir, name)
        }
    }
    if newest == "" {
        return "", fmt.Errorf("no .apk found in %s", dir)
    }
    return newest, nil
}

func copyFile(src, dst string) error {
    in, err := os.Open(src)
    if err != nil {
        return err
    }
    defer in.Close()

    out, err := os.Create(dst)
    if err != nil {
        return err
    }
    defer func() { _ = out.Close() }()

    if _, err = io.Copy(out, in); err != nil {
        return err
    }
    return out.Sync()
}