Parcourir la source

all: Add build constants for runtime.GOOS comparisons (#8442)

all: Add package runtimeos for runtime.GOOS comparisons

I grew tired of hand written string comparisons. This adds generated
constants for the GOOS values, and predefined Is$OS constants that can
be iffed on. In a couple of places I rewrote trivial switch:es to if:s,
and added Illumos where we checked for Solaris (because they are
effectively the same, and if we're going to target one of them that
would be Illumos...).
Jakob Borg il y a 3 ans
Parent
commit
a3c724f2c3
51 fichiers modifiés avec 280 ajouts et 190 suppressions
  1. 3 1
      build.go
  2. 1 2
      cmd/strelaysrv/main.go
  3. 1 1
      cmd/syncthing/main.go
  4. 3 3
      cmd/syncthing/monitor.go
  5. 8 10
      cmd/syncthing/openurl_unix.go
  6. 1 1
      lib/api/api.go
  7. 3 3
      lib/api/api_test.go
  8. 38 0
      lib/build/runtimeos.gen.go
  9. 43 0
      lib/build/runtimeos.sh
  10. 2 2
      lib/config/config.go
  11. 4 3
      lib/config/config_test.go
  12. 4 4
      lib/config/folderconfiguration.go
  13. 2 2
      lib/config/migrations.go
  14. 2 2
      lib/fs/basicfs.go
  15. 8 8
      lib/fs/basicfs_test.go
  16. 12 12
      lib/fs/basicfs_watch_test.go
  17. 4 2
      lib/fs/fakefs_test.go
  18. 6 5
      lib/fs/filesystem_test.go
  19. 4 4
      lib/fs/mtimefs_test.go
  20. 2 2
      lib/fs/tempname.go
  21. 4 3
      lib/fs/util.go
  22. 3 2
      lib/fs/util_test.go
  23. 5 4
      lib/fs/walkfs_test.go
  24. 2 2
      lib/ignore/ignore.go
  25. 6 7
      lib/ignore/ignore_test.go
  26. 21 22
      lib/locations/locations.go
  27. 4 4
      lib/model/folder_sendrecv.go
  28. 6 6
      lib/model/folder_sendrecv_test.go
  29. 2 2
      lib/model/folder_test.go
  30. 2 1
      lib/model/model.go
  31. 8 8
      lib/model/model_test.go
  32. 5 5
      lib/model/requests_test.go
  33. 4 4
      lib/model/utils_test.go
  34. 2 2
      lib/osutil/atomic.go
  35. 2 2
      lib/osutil/osutil.go
  36. 3 3
      lib/osutil/osutil_test.go
  37. 3 2
      lib/osutil/replacingwriter.go
  38. 3 2
      lib/osutil/rlimit_unix.go
  39. 3 3
      lib/osutil/traversessymlink_test.go
  40. 4 6
      lib/protocol/bep_extensions.go
  41. 2 2
      lib/protocol/protocol_test.go
  42. 4 4
      lib/scanner/walk.go
  43. 9 9
      lib/scanner/walk_test.go
  44. 6 6
      lib/upgrade/upgrade_common.go
  45. 3 1
      lib/upgrade/upgrade_test.go
  46. 2 2
      lib/upnp/upnp.go
  47. 2 2
      lib/versioner/external.go
  48. 2 2
      lib/versioner/external_test.go
  49. 2 2
      lib/watchaggregator/aggregator_test.go
  50. 3 2
      test/usage_unix.go
  51. 2 1
      test/util.go

+ 3 - 1
build.go

@@ -31,6 +31,8 @@ import (
 	"strings"
 	"text/template"
 	"time"
+
+	buildpkg "github.com/syncthing/syncthing/lib/build"
 )
 
 var (
@@ -393,7 +395,7 @@ func test(tags []string, pkgs ...string) {
 
 	if runtime.GOARCH == "amd64" {
 		switch runtime.GOOS {
-		case "darwin", "linux", "freebsd": // , "windows": # See https://github.com/golang/go/issues/27089
+		case buildpkg.Darwin, buildpkg.Linux, buildpkg.FreeBSD: // , "windows": # See https://github.com/golang/go/issues/27089
 			args = append(args, "-race")
 		}
 	}

+ 1 - 2
cmd/strelaysrv/main.go

@@ -14,7 +14,6 @@ import (
 	"os"
 	"os/signal"
 	"path/filepath"
-	"runtime"
 	"strings"
 	"sync/atomic"
 	"syscall"
@@ -146,7 +145,7 @@ func main() {
 		log.Println("Connection limit", descriptorLimit)
 
 		go monitorLimits()
-	} else if err != nil && runtime.GOOS != "windows" {
+	} else if err != nil && !build.IsWindows {
 		log.Println("Assuming no connection limit, due to error retrieving rlimits:", err)
 	}
 

+ 1 - 1
cmd/syncthing/main.go

@@ -199,7 +199,7 @@ func defaultVars() kong.Vars {
 	// Windows, the "default" options.logFile will later be replaced with the
 	// default path, unless the user has manually specified "-" or
 	// something else.
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		vars["logFile"] = "default"
 	} else {
 		vars["logFile"] = "-"

+ 3 - 3
cmd/syncthing/monitor.go

@@ -15,11 +15,11 @@ import (
 	"os/exec"
 	"os/signal"
 	"path/filepath"
-	"runtime"
 	"strings"
 	"syscall"
 	"time"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/locations"
 	"github.com/syncthing/syncthing/lib/osutil"
@@ -66,7 +66,7 @@ func monitorMain(options serveOptions) {
 		if err != nil {
 			l.Warnln("Failed to setup logging to file, proceeding with logging to stdout only:", err)
 		} else {
-			if runtime.GOOS == "windows" {
+			if build.IsWindows {
 				// Translate line breaks to Windows standard
 				fileDst = osutil.ReplacingWriter{
 					Writer: fileDst,
@@ -315,7 +315,7 @@ func restartMonitor(args []string) error {
 	// opening the browser on startup.
 	os.Setenv("STRESTART", "yes")
 
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		// syscall.Exec is the cleanest way to restart on Unixes as it
 		// replaces the current process with the new one, keeping the pid and
 		// controlling terminal and so on

+ 8 - 10
cmd/syncthing/openurl_unix.go

@@ -11,20 +11,18 @@ package main
 
 import (
 	"os/exec"
-	"runtime"
 	"syscall"
+
+	"github.com/syncthing/syncthing/lib/build"
 )
 
 func openURL(url string) error {
-	switch runtime.GOOS {
-	case "darwin":
+	if build.IsDarwin {
 		return exec.Command("open", url).Run()
-
-	default:
-		cmd := exec.Command("xdg-open", url)
-		cmd.SysProcAttr = &syscall.SysProcAttr{
-			Setpgid: true,
-		}
-		return cmd.Run()
 	}
+	cmd := exec.Command("xdg-open", url)
+	cmd.SysProcAttr = &syscall.SysProcAttr{
+		Setpgid: true,
+	}
+	return cmd.Run()
 }

+ 1 - 1
lib/api/api.go

@@ -1925,7 +1925,7 @@ func shouldRegenerateCertificate(cert tls.Certificate) error {
 	// On macOS, check for certificates issued on or after July 1st, 2019,
 	// with a longer validity time than 825 days.
 	cutoff := time.Date(2019, 7, 1, 0, 0, 0, 0, time.UTC)
-	if runtime.GOOS == "darwin" &&
+	if build.IsDarwin &&
 		leaf.NotBefore.After(cutoff) &&
 		leaf.NotAfter.Sub(leaf.NotBefore) > 825*24*time.Hour {
 		return errors.New("certificate incompatible with macOS 10.15 (Catalina)")

+ 3 - 3
lib/api/api_test.go

@@ -19,7 +19,6 @@ import (
 	"os"
 	"path/filepath"
 	"reflect"
-	"runtime"
 	"strconv"
 	"strings"
 	"testing"
@@ -27,6 +26,7 @@ import (
 
 	"github.com/d4l3k/messagediff"
 	"github.com/syncthing/syncthing/lib/assets"
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/config"
 	connmocks "github.com/syncthing/syncthing/lib/connections/mocks"
 	discovermocks "github.com/syncthing/syncthing/lib/discover/mocks"
@@ -1255,7 +1255,7 @@ func TestShouldRegenerateCertificate(t *testing.T) {
 		t.Error("expected no error:", err)
 	}
 
-	if runtime.GOOS == "darwin" {
+	if build.IsDarwin {
 		// Certificates with too long an expiry time are not allowed on macOS
 		crt, err = tlsutil.NewCertificateInMemory("foo.example.com", 1000)
 		if err != nil {
@@ -1416,7 +1416,7 @@ func TestSanitizedHostname(t *testing.T) {
 // be prone to false negatives if things change in the future, but likely
 // not false positives.
 func runningInContainer() bool {
-	if runtime.GOOS != "linux" {
+	if !build.IsLinux {
 		return false
 	}
 

+ 38 - 0
lib/build/runtimeos.gen.go

@@ -0,0 +1,38 @@
+// Code generated by runtimeos.sh. DO NOT EDIT.
+package build
+
+import "runtime"
+
+const (
+	AIX       = "aix"
+	Android   = "android"
+	Darwin    = "darwin"
+	Dragonfly = "dragonfly"
+	FreeBSD   = "freebsd"
+	Illumos   = "illumos"
+	IOS       = "ios"
+	JS        = "js"
+	Linux     = "linux"
+	NetBSD    = "netbsd"
+	OpenBSD   = "openbsd"
+	Plan9     = "plan9"
+	Solaris   = "solaris"
+	Windows   = "windows"
+)
+
+const (
+	IsAIX       = runtime.GOOS == AIX
+	IsAndroid   = runtime.GOOS == Android
+	IsDarwin    = runtime.GOOS == Darwin
+	IsDragonfly = runtime.GOOS == Dragonfly
+	IsFreeBSD   = runtime.GOOS == FreeBSD
+	IsIllumos   = runtime.GOOS == Illumos
+	IsIOS       = runtime.GOOS == IOS
+	IsJS        = runtime.GOOS == JS
+	IsLinux     = runtime.GOOS == Linux
+	IsNetBSD    = runtime.GOOS == NetBSD
+	IsOpenBSD   = runtime.GOOS == OpenBSD
+	IsPlan9     = runtime.GOOS == Plan9
+	IsSolaris   = runtime.GOOS == Solaris
+	IsWindows   = runtime.GOOS == Windows
+)

+ 43 - 0
lib/build/runtimeos.sh

@@ -0,0 +1,43 @@
+# Copyright (C) 2022 The Syncthing Authors.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+#!/bin/sh
+
+# List known operating system/architecture combos, removing the
+# architecture, and making constants of them.
+
+osname() {
+    echo "$1" | awk '{print toupper(substr($1, 1, 1)) substr($1, 2)}' \
+        | sed s/aix/AIX/i \
+        | sed s/bsd/BSD/i \
+        | sed s/ios/IOS/i \
+        | sed s/js/JS/i
+}
+
+osconstants() {
+    echo "// Code generated by runtimeos.sh. DO NOT EDIT."
+    echo "package build"
+    echo "import \"runtime\""
+
+    echo "const ("
+    for OS in $(go tool dist list | sed 's|/.*||' | sort | uniq) ; do
+        name=$(osname "$OS")
+        echo "$name = \"$OS\""
+    done
+    echo ")"
+    echo
+
+    echo "const ("
+    for OS in $(go tool dist list | sed 's|/.*||' | sort | uniq) ; do
+        name=$(osname "$OS")
+        echo "Is$name = runtime.GOOS == $name"
+    done
+    echo ")"
+}
+
+osconstants > runtimeos.gen.go
+
+gofmt -w -s runtimeos.gen.go

+ 2 - 2
lib/config/config.go

@@ -15,13 +15,13 @@ import (
 	"net"
 	"net/url"
 	"os"
-	"runtime"
 	"sort"
 	"strconv"
 	"strings"
 
 	"github.com/pkg/errors"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/util"
@@ -549,7 +549,7 @@ loop:
 }
 
 func cleanSymlinks(filesystem fs.Filesystem, dir string) {
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		// We don't do symlinks on Windows. Additionally, there may
 		// be things that look like symlinks that are not, which we
 		// should leave alone. Deduplicated files, for example.

+ 4 - 3
lib/config/config_test.go

@@ -23,6 +23,7 @@ import (
 
 	"github.com/d4l3k/messagediff"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/protocol"
@@ -445,7 +446,7 @@ func TestVersioningConfig(t *testing.T) {
 }
 
 func TestIssue1262(t *testing.T) {
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skipf("path gets converted to absolute as part of the filesystem initialization on linux")
 	}
 
@@ -538,7 +539,7 @@ func TestFolderCheckPath(t *testing.T) {
 			path: "link",
 			err:  nil,
 		})
-	} else if runtime.GOOS != "windows" {
+	} else if !build.IsWindows {
 		t.Log("running without symlink check")
 		t.Fatal(err)
 	}
@@ -593,7 +594,7 @@ func TestNewSaveLoad(t *testing.T) {
 }
 
 func TestWindowsLineEndings(t *testing.T) {
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skip("Windows specific")
 	}
 

+ 4 - 4
lib/config/folderconfiguration.go

@@ -9,13 +9,13 @@ package config
 import (
 	"errors"
 	"fmt"
-	"runtime"
 	"sort"
 	"strings"
 	"time"
 
 	"github.com/shirou/gopsutil/v3/disk"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/db"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/protocol"
@@ -64,7 +64,7 @@ func (f FolderConfiguration) Filesystem(fset *db.FileSet) fs.Filesystem {
 
 func (f FolderConfiguration) ModTimeWindow() time.Duration {
 	dur := time.Duration(f.RawModTimeWindowS) * time.Second
-	if f.RawModTimeWindowS < 1 && runtime.GOOS == "android" {
+	if f.RawModTimeWindowS < 1 && build.IsAndroid {
 		if usage, err := disk.Usage(f.Filesystem(nil).URI()); err != nil {
 			dur = 2 * time.Second
 			l.Debugf(`Detecting FS at "%v" on android: Setting mtime window to 2s: err == "%v"`, f.Path, err)
@@ -90,7 +90,7 @@ func (f *FolderConfiguration) CreateMarker() error {
 	}
 
 	permBits := fs.FileMode(0777)
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		// Windows has no umask so we must chose a safer set of bits to
 		// begin with.
 		permBits = 0700
@@ -145,7 +145,7 @@ func (f *FolderConfiguration) CreateRoot() (err error) {
 	// Directory permission bits. Will be filtered down to something
 	// sane by umask on Unixes.
 	permBits := fs.FileMode(0777)
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		// Windows has no umask so we must chose a safer set of bits to
 		// begin with.
 		permBits = 0700

+ 2 - 2
lib/config/migrations.go

@@ -11,11 +11,11 @@ import (
 	"os"
 	"path"
 	"path/filepath"
-	"runtime"
 	"sort"
 	"strings"
 	"sync"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/upgrade"
 	"github.com/syncthing/syncthing/lib/util"
@@ -189,7 +189,7 @@ func migrateToConfigV24(cfg *Configuration) {
 
 func migrateToConfigV23(cfg *Configuration) {
 	permBits := fs.FileMode(0777)
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		// Windows has no umask so we must chose a safer set of bits to
 		// begin with.
 		permBits = 0700

+ 2 - 2
lib/fs/basicfs.go

@@ -11,11 +11,11 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
-	"runtime"
 	"strings"
 	"time"
 
 	"github.com/shirou/gopsutil/v3/disk"
+	"github.com/syncthing/syncthing/lib/build"
 )
 
 var (
@@ -79,7 +79,7 @@ func newBasicFilesystem(root string, opts ...Option) *BasicFilesystem {
 
 	// Attempt to enable long filename support on Windows. We may still not
 	// have an absolute path here if the previous steps failed.
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		root = longFilenameSupport(root)
 	}
 

+ 8 - 8
lib/fs/basicfs_test.go

@@ -9,13 +9,13 @@ package fs
 import (
 	"os"
 	"path/filepath"
-	"runtime"
 	"sort"
 	"strconv"
 	"strings"
 	"testing"
 	"time"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/rand"
 )
 
@@ -55,7 +55,7 @@ func TestChmodFile(t *testing.T) {
 }
 
 func TestChownFile(t *testing.T) {
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		t.Skip("Not supported on Windows")
 		return
 	}
@@ -106,7 +106,7 @@ func TestChmodDir(t *testing.T) {
 	path := filepath.Join(dir, "dir")
 
 	mode := os.FileMode(0755)
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		mode = os.FileMode(0777)
 	}
 
@@ -177,7 +177,7 @@ func TestCreate(t *testing.T) {
 }
 
 func TestCreateSymlink(t *testing.T) {
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		t.Skip("windows not supported")
 	}
 
@@ -336,7 +336,7 @@ func TestUsage(t *testing.T) {
 	fs, _ := setup(t)
 	usage, err := fs.Usage(".")
 	if err != nil {
-		if runtime.GOOS == "netbsd" || runtime.GOOS == "openbsd" || runtime.GOOS == "solaris" {
+		if build.IsNetBSD || build.IsOpenBSD || build.IsSolaris || build.IsIllumos {
 			t.Skip()
 		}
 		t.Errorf("Unexpected error: %s", err)
@@ -429,7 +429,7 @@ func TestRooted(t *testing.T) {
 		{"/", ".", "/", true},
 	}
 
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		extraCases := []testcase{
 			{`c:\`, `foo`, `c:\foo`, true},
 			{`\\?\c:\`, `foo`, `\\?\c:\foo`, true},
@@ -503,7 +503,7 @@ func TestRooted(t *testing.T) {
 }
 
 func TestNewBasicFilesystem(t *testing.T) {
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		t.Skip("non-windows root paths")
 	}
 
@@ -550,7 +550,7 @@ func TestRel(t *testing.T) {
 		{"/", "/Test", "Test"},
 		{"/Test", "/Test/test", "test"},
 	}
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		for i := range testCases {
 			testCases[i].root = filepath.FromSlash(testCases[i].root)
 			testCases[i].abs = filepath.FromSlash(testCases[i].abs)

+ 12 - 12
lib/fs/basicfs_watch_test.go

@@ -15,7 +15,6 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
-	"runtime"
 	"strconv"
 	"strings"
 	"syscall"
@@ -23,6 +22,7 @@ import (
 	"time"
 
 	"github.com/syncthing/notify"
+	"github.com/syncthing/syncthing/lib/build"
 )
 
 func TestMain(m *testing.M) {
@@ -41,7 +41,7 @@ func TestMain(m *testing.M) {
 	}
 
 	testDirAbs = filepath.Join(dir, testDir)
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		testDirAbs = longFilenameSupport(testDirAbs)
 	}
 
@@ -68,7 +68,7 @@ var (
 )
 
 func TestWatchIgnore(t *testing.T) {
-	if runtime.GOOS == "openbsd" {
+	if build.IsOpenBSD {
 		t.Skip(failsOnOpenBSD)
 	}
 	name := "ignore"
@@ -92,7 +92,7 @@ func TestWatchIgnore(t *testing.T) {
 }
 
 func TestWatchInclude(t *testing.T) {
-	if runtime.GOOS == "openbsd" {
+	if build.IsOpenBSD {
 		t.Skip(failsOnOpenBSD)
 	}
 	name := "include"
@@ -119,7 +119,7 @@ func TestWatchInclude(t *testing.T) {
 }
 
 func TestWatchRename(t *testing.T) {
-	if runtime.GOOS == "openbsd" {
+	if build.IsOpenBSD {
 		t.Skip(failsOnOpenBSD)
 	}
 	name := "rename"
@@ -134,7 +134,7 @@ func TestWatchRename(t *testing.T) {
 	destEvent := Event{new, Remove}
 	// Only on these platforms the removed file can be differentiated from
 	// the created file during renaming
-	if runtime.GOOS == "windows" || runtime.GOOS == "linux" || runtime.GOOS == "solaris" || runtime.GOOS == "freebsd" {
+	if build.IsWindows || build.IsLinux || build.IsSolaris || build.IsIllumos || build.IsFreeBSD {
 		destEvent = Event{new, NonRemove}
 	}
 	expectedEvents := []Event{
@@ -154,7 +154,7 @@ func TestWatchRename(t *testing.T) {
 // out of root event on every event.
 // https://github.com/syncthing/syncthing/issues/5695
 func TestWatchWinRoot(t *testing.T) {
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skip("Windows specific test")
 	}
 
@@ -288,7 +288,7 @@ func TestWatchSubpath(t *testing.T) {
 
 // TestWatchOverflow checks that an event at the root is sent when maxFiles is reached
 func TestWatchOverflow(t *testing.T) {
-	if runtime.GOOS == "openbsd" {
+	if build.IsOpenBSD {
 		t.Skip(failsOnOpenBSD)
 	}
 	name := "overflow"
@@ -313,7 +313,7 @@ func TestWatchOverflow(t *testing.T) {
 }
 
 func TestWatchErrorLinuxInterpretation(t *testing.T) {
-	if runtime.GOOS != "linux" {
+	if !build.IsLinux {
 		t.Skip("testing of linux specific error codes")
 	}
 
@@ -341,7 +341,7 @@ func TestWatchErrorLinuxInterpretation(t *testing.T) {
 }
 
 func TestWatchSymlinkedRoot(t *testing.T) {
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		t.Skip("Involves symlinks")
 	}
 
@@ -385,7 +385,7 @@ func TestUnrootedChecked(t *testing.T) {
 }
 
 func TestWatchIssue4877(t *testing.T) {
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skip("Windows specific test")
 	}
 
@@ -440,7 +440,7 @@ func TestWatchModTime(t *testing.T) {
 
 	var allowedEvents []Event
 	// Apparently an event for the parent is also sent on mac
-	if runtime.GOOS == "darwin" {
+	if build.IsDarwin {
 		allowedEvents = []Event{
 			{name, NonRemove},
 		}

+ 4 - 2
lib/fs/fakefs_test.go

@@ -17,6 +17,8 @@ import (
 	"sort"
 	"testing"
 	"time"
+
+	"github.com/syncthing/syncthing/lib/build"
 )
 
 func TestFakeFS(t *testing.T) {
@@ -563,7 +565,7 @@ func testFakeFSRenameInsensitive(t *testing.T, fs Filesystem) {
 	}
 
 	// not checking on darwin due to https://github.com/golang/go/issues/35222
-	if runtime.GOOS != "darwin" {
+	if !build.IsDarwin {
 		if err := fs.Rename("/foo/bar/BAZ", "/FOO/BAR/bAz"); err != nil {
 			t.Errorf("Could not perform in-place case-only directory rename: %s", err)
 		}
@@ -786,7 +788,7 @@ func testFakeFSSameFile(t *testing.T, fs Filesystem) {
 			t.Fatalf("Could not create %s: %s", filename, err)
 		} else {
 			fd.Close()
-			if runtime.GOOS == "windows" {
+			if build.IsWindows {
 				time.Sleep(1 * time.Millisecond)
 			}
 		}

+ 6 - 5
lib/fs/filesystem_test.go

@@ -8,8 +8,9 @@ package fs
 
 import (
 	"path/filepath"
-	"runtime"
 	"testing"
+
+	"github.com/syncthing/syncthing/lib/build"
 )
 
 func TestIsInternal(t *testing.T) {
@@ -120,7 +121,7 @@ func TestIsParent(t *testing.T) {
 	testBoth := func(path, parent string, expected bool) {
 		t.Helper()
 		test(path, parent, expected)
-		if runtime.GOOS == "windows" {
+		if build.IsWindows {
 			test("C:/"+path, "C:/"+parent, expected)
 		} else {
 			test("/"+path, "/"+parent, expected)
@@ -130,7 +131,7 @@ func TestIsParent(t *testing.T) {
 	// rel - abs
 	for _, parent := range []string{"/", "/foo", "/foo/bar"} {
 		for _, path := range []string{"", ".", "foo", "foo/bar", "bas", "bas/baz"} {
-			if runtime.GOOS == "windows" {
+			if build.IsWindows {
 				parent = "C:/" + parent
 			}
 			test(parent, path, false)
@@ -140,7 +141,7 @@ func TestIsParent(t *testing.T) {
 
 	// equal
 	for i, path := range []string{"/", "/foo", "/foo/bar", "", ".", "foo", "foo/bar"} {
-		if i < 3 && runtime.GOOS == "windows" {
+		if i < 3 && build.IsWindows {
 			path = "C:" + path
 		}
 		test(path, path, false)
@@ -164,7 +165,7 @@ func TestIsParent(t *testing.T) {
 		for _, path := range []string{"foo/bar/baz", "foo/bar/baz/bas"} {
 			testBoth(path, parent, true)
 			testBoth(parent, path, false)
-			if runtime.GOOS == "windows" {
+			if build.IsWindows {
 				test("C:/"+path, "D:/"+parent, false)
 			}
 		}

+ 4 - 4
lib/fs/mtimefs_test.go

@@ -10,9 +10,10 @@ import (
 	"errors"
 	"os"
 	"path/filepath"
-	"runtime"
 	"testing"
 	"time"
+
+	"github.com/syncthing/syncthing/lib/build"
 )
 
 func TestMtimeFS(t *testing.T) {
@@ -181,11 +182,10 @@ func TestMtimeFSOpen(t *testing.T) {
 }
 
 func TestMtimeFSInsensitive(t *testing.T) {
-	switch runtime.GOOS {
-	case "darwin", "windows":
+	if build.IsDarwin || build.IsWindows {
 		// blatantly assume file systems here are case insensitive. Might be
 		// a spurious failure on oddly configured systems.
-	default:
+	} else {
 		t.Skip("need case insensitive FS")
 	}
 

+ 2 - 2
lib/fs/tempname.go

@@ -9,9 +9,9 @@ package fs
 import (
 	"fmt"
 	"path/filepath"
-	"runtime"
 	"strings"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/sha256"
 )
 
@@ -21,7 +21,7 @@ const (
 )
 
 func tempPrefix() string {
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		return WindowsTempPrefix
 	} else {
 		return UnixTempPrefix

+ 4 - 3
lib/fs/util.go

@@ -10,9 +10,10 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
-	"runtime"
 	"strings"
 	"unicode"
+
+	"github.com/syncthing/syncthing/lib/build"
 )
 
 const pathSeparatorString = string(PathSeparator)
@@ -35,7 +36,7 @@ func ExpandTilde(path string) (string, error) {
 }
 
 func getHomeDir() (string, error) {
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		// Legacy -- we prioritize this for historical reasons, whereas
 		// os.UserHomeDir uses %USERPROFILE% always.
 		home := filepath.Join(os.Getenv("HomeDrive"), os.Getenv("HomePath"))
@@ -196,7 +197,7 @@ func CommonPrefix(first, second string) string {
 	}
 
 	if isAbs {
-		if runtime.GOOS == "windows" && isVolumeNameOnly(common) {
+		if build.IsWindows && isVolumeNameOnly(common) {
 			// Because strings.Split strips out path separators, if we're at the volume name, we end up without a separator
 			// Wedge an empty element to be joined with.
 			common = append(common, "")

+ 3 - 2
lib/fs/util_test.go

@@ -8,10 +8,11 @@ package fs
 
 import (
 	"math/rand"
-	"runtime"
 	"testing"
 	"unicode"
 	"unicode/utf8"
+
+	"github.com/syncthing/syncthing/lib/build"
 )
 
 func TestCommonPrefix(t *testing.T) {
@@ -23,7 +24,7 @@ func TestCommonPrefix(t *testing.T) {
 		}
 	}
 
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		test(`c:\Audrius\Downloads`, `c:\Audrius\Docs`, `c:\Audrius`)
 		test(`c:\Audrius\Downloads`, `C:\Audrius\Docs`, ``) // Case differences :(
 		test(`C:\Audrius-a\Downloads`, `C:\Audrius-b\Docs`, `C:\`)

+ 5 - 4
lib/fs/walkfs_test.go

@@ -11,12 +11,13 @@ import (
 	"fmt"
 	osexec "os/exec"
 	"path/filepath"
-	"runtime"
 	"testing"
+
+	"github.com/syncthing/syncthing/lib/build"
 )
 
 func testWalkSkipSymlink(t *testing.T, fsType FilesystemType, uri string) {
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		t.Skip("Symlinks skipping is not tested on windows")
 	}
 
@@ -53,7 +54,7 @@ func createDirJunct(target string, name string) error {
 }
 
 func testWalkTraverseDirJunct(t *testing.T, fsType FilesystemType, uri string) {
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skip("Directory junctions are available and tested on windows only")
 	}
 
@@ -86,7 +87,7 @@ func testWalkTraverseDirJunct(t *testing.T, fsType FilesystemType, uri string) {
 }
 
 func testWalkInfiniteRecursion(t *testing.T, fsType FilesystemType, uri string) {
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skip("Infinite recursion detection is tested on windows only")
 	}
 

+ 2 - 2
lib/ignore/ignore.go

@@ -13,12 +13,12 @@ import (
 	"fmt"
 	"io"
 	"path/filepath"
-	"runtime"
 	"strings"
 	"time"
 
 	"github.com/gobwas/glob"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/osutil"
 	"github.com/syncthing/syncthing/lib/sha256"
@@ -35,7 +35,7 @@ const (
 var defaultResult Result = resultInclude
 
 func init() {
-	if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
+	if build.IsDarwin || build.IsWindows {
 		defaultResult |= resultFoldCase
 	}
 }

+ 6 - 7
lib/ignore/ignore_test.go

@@ -12,11 +12,11 @@ import (
 	"io"
 	"os"
 	"path/filepath"
-	"runtime"
 	"strings"
 	"testing"
 	"time"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/osutil"
 )
@@ -210,10 +210,9 @@ func TestCaseSensitivity(t *testing.T) {
 	match := []string{"test"}
 	dontMatch := []string{"foo"}
 
-	switch runtime.GOOS {
-	case "darwin", "windows":
+	if build.IsDarwin || build.IsWindows {
 		match = append(match, "TEST", "Test", "tESt")
-	default:
+	} else {
 		dontMatch = append(dontMatch, "TEST", "Test", "tESt")
 	}
 
@@ -618,7 +617,7 @@ func TestHashOfEmpty(t *testing.T) {
 func TestWindowsPatterns(t *testing.T) {
 	// We should accept patterns as both a/b and a\b and match that against
 	// both kinds of slash as well.
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skip("Windows specific test")
 		return
 	}
@@ -643,7 +642,7 @@ func TestWindowsPatterns(t *testing.T) {
 
 func TestAutomaticCaseInsensitivity(t *testing.T) {
 	// We should do case insensitive matching by default on some platforms.
-	if runtime.GOOS != "windows" && runtime.GOOS != "darwin" {
+	if !build.IsWindows && !build.IsDarwin {
 		t.Skip("Windows/Mac specific test")
 		return
 	}
@@ -1205,7 +1204,7 @@ func TestEmptyPatterns(t *testing.T) {
 }
 
 func TestWindowsLineEndings(t *testing.T) {
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skip("Windows specific")
 	}
 

+ 21 - 22
lib/locations/locations.go

@@ -14,6 +14,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/fs"
 )
 
@@ -133,13 +134,13 @@ func expandLocations() error {
 // trying.
 func defaultConfigDir(userHome string) string {
 	switch runtime.GOOS {
-	case "windows":
+	case build.Windows:
 		if p := os.Getenv("LocalAppData"); p != "" {
 			return filepath.Join(p, "Syncthing")
 		}
 		return filepath.Join(os.Getenv("AppData"), "Syncthing")
 
-	case "darwin":
+	case build.Darwin:
 		return filepath.Join(userHome, "Library/Application Support/Syncthing")
 
 	default:
@@ -153,30 +154,28 @@ func defaultConfigDir(userHome string) string {
 // defaultDataDir returns the default data directory, which usually is the
 // config directory but might be something else.
 func defaultDataDir(userHome, config string) string {
-	switch runtime.GOOS {
-	case "windows", "darwin":
+	if build.IsWindows || build.IsDarwin {
 		return config
+	}
 
-	default:
-		// If a database exists at the "normal" location, use that anyway.
-		if _, err := os.Lstat(filepath.Join(config, LevelDBDir)); err == nil {
-			return config
-		}
-		// Always use this env var, as it's explicitly set by the user
-		if xdgHome := os.Getenv("XDG_DATA_HOME"); xdgHome != "" {
-			return filepath.Join(xdgHome, "syncthing")
-		}
-		// Only use the XDG default, if a syncthing specific dir already
-		// exists. Existence of ~/.local/share is not deemed enough, as
-		// it may also exist erroneously on non-XDG systems.
-		xdgDefault := filepath.Join(userHome, ".local/share/syncthing")
-		if _, err := os.Lstat(xdgDefault); err == nil {
-			return xdgDefault
-		}
-		// FYI: XDG_DATA_DIRS is not relevant, as it is for system-wide
-		// data dirs, not user specific ones.
+	// If a database exists at the "normal" location, use that anyway.
+	if _, err := os.Lstat(filepath.Join(config, LevelDBDir)); err == nil {
 		return config
 	}
+	// Always use this env var, as it's explicitly set by the user
+	if xdgHome := os.Getenv("XDG_DATA_HOME"); xdgHome != "" {
+		return filepath.Join(xdgHome, "syncthing")
+	}
+	// Only use the XDG default, if a syncthing specific dir already
+	// exists. Existence of ~/.local/share is not deemed enough, as
+	// it may also exist erroneously on non-XDG systems.
+	xdgDefault := filepath.Join(userHome, ".local/share/syncthing")
+	if _, err := os.Lstat(xdgDefault); err == nil {
+		return xdgDefault
+	}
+	// FYI: XDG_DATA_DIRS is not relevant, as it is for system-wide
+	// data dirs, not user specific ones.
+	return config
 }
 
 // userHomeDir returns the user's home directory, or dies trying.

+ 4 - 4
lib/model/folder_sendrecv.go

@@ -11,7 +11,6 @@ import (
 	"fmt"
 	"io"
 	"path/filepath"
-	"runtime"
 	"sort"
 	"strconv"
 	"strings"
@@ -19,6 +18,7 @@ import (
 
 	"github.com/pkg/errors"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/db"
 	"github.com/syncthing/syncthing/lib/events"
@@ -344,7 +344,7 @@ func (f *sendReceiveFolder) processNeeded(snap *db.Snapshot, dbUpdateChan chan<-
 			l.Debugln(f, "Handling ignored file", file)
 			dbUpdateChan <- dbUpdateJob{file, dbUpdateInvalidate}
 
-		case runtime.GOOS == "windows" && fs.WindowsInvalidFilename(file.Name) != nil:
+		case build.IsWindows && fs.WindowsInvalidFilename(file.Name) != nil:
 			if file.IsDeleted() {
 				// Just pretend we deleted it, no reason to create an error
 				// about a deleted file that we can't have anyway.
@@ -394,7 +394,7 @@ func (f *sendReceiveFolder) processNeeded(snap *db.Snapshot, dbUpdateChan chan<-
 				f.queue.Push(file.Name, file.Size, file.ModTime())
 			}
 
-		case runtime.GOOS == "windows" && file.IsSymlink():
+		case build.IsWindows && file.IsSymlink():
 			if err := f.handleSymlinkCheckExisting(file, snap, scanChan); err != nil {
 				f.newPullError(file.Name, fmt.Errorf("handling unsupported symlink: %w", err))
 				break
@@ -2115,7 +2115,7 @@ func (f *sendReceiveFolder) maybeAdjustOwnership(file *protocol.FileInfo, name s
 }
 
 func (f *sendReceiveFolder) copyOwnershipFromParent(path string) error {
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		// Can't do anything.
 		return nil
 	}

+ 6 - 6
lib/model/folder_sendrecv_test.go

@@ -15,12 +15,12 @@ import (
 	"io"
 	"os"
 	"path/filepath"
-	"runtime"
 	"strconv"
 	"strings"
 	"testing"
 	"time"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/ignore"
@@ -205,7 +205,7 @@ func TestHandleFileWithTemp(t *testing.T) {
 
 func TestCopierFinder(t *testing.T) {
 	methods := []fs.CopyRangeMethod{fs.CopyRangeMethodStandard, fs.CopyRangeMethodAllWithFallback}
-	if runtime.GOOS == "linux" {
+	if build.IsLinux {
 		methods = append(methods, fs.CopyRangeMethodSendFile)
 	}
 	for _, method := range methods {
@@ -789,7 +789,7 @@ func TestCopyOwner(t *testing.T) {
 	// Verifies that owner and group are copied from the parent, for both
 	// files and directories.
 
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		t.Skip("copying owner not supported on Windows")
 	}
 
@@ -986,7 +986,7 @@ func TestDeleteBehindSymlink(t *testing.T) {
 	must(t, ffs.RemoveAll(link))
 
 	if err := fs.DebugSymlinkForTestsOnly(destFs, ffs, "", link); err != nil {
-		if runtime.GOOS == "windows" {
+		if build.IsWindows {
 			// Probably we require permissions we don't have.
 			t.Skip("Need admin permissions or developer mode to run symlink test on Windows: " + err.Error())
 		} else {
@@ -1145,7 +1145,7 @@ func TestPullCaseOnlyDir(t *testing.T) {
 }
 
 func TestPullCaseOnlySymlink(t *testing.T) {
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		t.Skip("symlinks not supported on windows")
 	}
 	testPullCaseOnlyDirOrSymlink(t, false)
@@ -1275,7 +1275,7 @@ func TestPullCaseOnlyRename(t *testing.T) {
 }
 
 func TestPullSymlinkOverExistingWindows(t *testing.T) {
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skip()
 	}
 

+ 2 - 2
lib/model/folder_test.go

@@ -8,11 +8,11 @@ package model
 
 import (
 	"path/filepath"
-	"runtime"
 	"testing"
 
 	"github.com/d4l3k/messagediff"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/config"
 )
 
@@ -96,7 +96,7 @@ func unifySubsCases() []unifySubsCase {
 		},
 	}
 
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		// Fixup path separators
 		for i := range cases {
 			for j, p := range cases[i].in {

+ 2 - 1
lib/model/model.go

@@ -27,6 +27,7 @@ import (
 	"github.com/pkg/errors"
 	"github.com/thejerf/suture/v4"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/connections"
 	"github.com/syncthing/syncthing/lib/db"
@@ -2399,7 +2400,7 @@ func (m *model) numHashers(folder string) int {
 		return folderCfg.Hashers
 	}
 
-	if runtime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "android" {
+	if build.IsWindows || build.IsDarwin || build.IsAndroid {
 		// Interactive operating systems; don't load the system too heavily by
 		// default.
 		return 1

+ 8 - 8
lib/model/model_test.go

@@ -15,7 +15,6 @@ import (
 	"math/rand"
 	"os"
 	"path/filepath"
-	"runtime"
 	"runtime/pprof"
 	"sort"
 	"strconv"
@@ -26,6 +25,7 @@ import (
 	"time"
 
 	"github.com/pkg/errors"
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/db"
 	"github.com/syncthing/syncthing/lib/db/backend"
@@ -1463,7 +1463,7 @@ func changeIgnores(t *testing.T, m *testModel, expected []string) {
 		t.Errorf("Incorrect ignores: %v != %v", ignores2, ignores)
 	}
 
-	if runtime.GOOS == "darwin" {
+	if build.IsDarwin {
 		// see above
 		time.Sleep(time.Second)
 	} else {
@@ -2130,7 +2130,7 @@ func TestIssue4357(t *testing.T) {
 func TestIssue2782(t *testing.T) {
 	// CheckHealth should accept a symlinked folder, when using tilde-expanded path.
 
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		t.Skip("not reliable on Windows")
 		return
 	}
@@ -2471,7 +2471,7 @@ func TestNoRequestsFromPausedDevices(t *testing.T) {
 
 // TestIssue2571 tests replacing a directory with content with a symlink
 func TestIssue2571(t *testing.T) {
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		t.Skip("Scanning symlinks isn't supported on windows")
 	}
 
@@ -2510,7 +2510,7 @@ func TestIssue2571(t *testing.T) {
 
 // TestIssue4573 tests that contents of an unavailable dir aren't marked deleted
 func TestIssue4573(t *testing.T) {
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		t.Skip("Can't make the dir inaccessible on windows")
 	}
 
@@ -2802,7 +2802,7 @@ func TestVersionRestore(t *testing.T) {
 		"dir/file.txt",
 		"dir/existing.txt",
 	} {
-		if runtime.GOOS == "windows" {
+		if build.IsWindows {
 			file = filepath.FromSlash(file)
 		}
 		dir := filepath.Dir(file)
@@ -2886,7 +2886,7 @@ func TestVersionRestore(t *testing.T) {
 
 	// Check that content of files matches to the version they've been restored.
 	for file, version := range restore {
-		if runtime.GOOS == "windows" {
+		if build.IsWindows {
 			file = filepath.FromSlash(file)
 		}
 		tag := version.In(time.Local).Truncate(time.Second).Format(versioner.TimeFormat)
@@ -2918,7 +2918,7 @@ func TestVersionRestore(t *testing.T) {
 	must(t, err)
 	for file, versions := range allFileVersions {
 		key := file
-		if runtime.GOOS == "windows" {
+		if build.IsWindows {
 			file = filepath.FromSlash(file)
 		}
 		for _, version := range versions {

+ 5 - 5
lib/model/requests_test.go

@@ -12,13 +12,13 @@ import (
 	"errors"
 	"os"
 	"path/filepath"
-	"runtime"
 	"strconv"
 	"strings"
 	"sync"
 	"testing"
 	"time"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/fs"
@@ -72,7 +72,7 @@ func TestRequestSimple(t *testing.T) {
 func TestSymlinkTraversalRead(t *testing.T) {
 	// Verify that a symlink can not be traversed for reading.
 
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		t.Skip("no symlink support on CI")
 		return
 	}
@@ -115,7 +115,7 @@ func TestSymlinkTraversalRead(t *testing.T) {
 func TestSymlinkTraversalWrite(t *testing.T) {
 	// Verify that a symlink can not be traversed for writing.
 
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		t.Skip("no symlink support on CI")
 		return
 	}
@@ -214,7 +214,7 @@ func TestRequestCreateTmpSymlink(t *testing.T) {
 }
 
 func TestRequestVersioningSymlinkAttack(t *testing.T) {
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		t.Skip("no symlink support on Windows")
 	}
 
@@ -613,7 +613,7 @@ func TestParentDeletion(t *testing.T) {
 // TestRequestSymlinkWindows checks that symlinks aren't marked as deleted on windows
 // Issue: https://github.com/syncthing/syncthing/issues/5125
 func TestRequestSymlinkWindows(t *testing.T) {
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skip("windows specific test")
 	}
 

+ 4 - 4
lib/model/utils_test.go

@@ -7,9 +7,9 @@
 package model
 
 import (
-	"runtime"
 	"testing"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/fs"
 )
 
@@ -70,7 +70,7 @@ func TestInWriteableDir(t *testing.T) {
 func TestOSWindowsRemove(t *testing.T) {
 	// os.Remove should remove read only things on windows
 
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skipf("Tests not required")
 		return
 	}
@@ -107,7 +107,7 @@ func TestOSWindowsRemove(t *testing.T) {
 func TestOSWindowsRemoveAll(t *testing.T) {
 	// os.RemoveAll should remove read only things on windows
 
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skipf("Tests not required")
 		return
 	}
@@ -139,7 +139,7 @@ func TestOSWindowsRemoveAll(t *testing.T) {
 }
 
 func TestInWritableDirWindowsRename(t *testing.T) {
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skipf("Tests not required")
 		return
 	}

+ 2 - 2
lib/osutil/atomic.go

@@ -9,8 +9,8 @@ package osutil
 import (
 	"errors"
 	"path/filepath"
-	"runtime"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/fs"
 )
 
@@ -97,7 +97,7 @@ func (w *AtomicWriter) Close() error {
 		return infoErr
 	}
 	err := w.fs.Rename(w.next.Name(), w.path)
-	if runtime.GOOS == "windows" && fs.IsPermission(err) {
+	if build.IsWindows && fs.IsPermission(err) {
 		// On Windows, we might not be allowed to rename over the file
 		// because it's read-only. Get us some write permissions and try
 		// again.

+ 2 - 2
lib/osutil/osutil.go

@@ -9,9 +9,9 @@ package osutil
 
 import (
 	"path/filepath"
-	"runtime"
 	"strings"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/sync"
 )
@@ -90,7 +90,7 @@ func withPreparedTarget(filesystem fs.Filesystem, from, to string, f func() erro
 	}
 
 	// On Windows, make sure the destination file is writeable (or we can't delete it)
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		filesystem.Chmod(to, 0666)
 		if !strings.EqualFold(from, to) {
 			err := filesystem.Remove(to)

+ 3 - 3
lib/osutil/osutil_test.go

@@ -10,10 +10,10 @@ import (
 	"io"
 	"os"
 	"path/filepath"
-	"runtime"
 	"strings"
 	"testing"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/osutil"
 )
@@ -51,7 +51,7 @@ func TestIsDeleted(t *testing.T) {
 		}
 		fd.Close()
 	}
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		// Can't create unreadable dir on windows
 		testFs.MkdirAll("inacc", 0777)
 		if err := testFs.Chmod("inacc", 0000); err == nil {
@@ -63,7 +63,7 @@ func TestIsDeleted(t *testing.T) {
 	}
 	for _, n := range []string{"Dir", "File", "Del"} {
 		if err := fs.DebugSymlinkForTestsOnly(testFs, testFs, strings.ToLower(n), "linkTo"+n); err != nil {
-			if runtime.GOOS == "windows" {
+			if build.IsWindows {
 				t.Skip("Symlinks aren't working")
 			}
 			t.Fatal(err)

+ 3 - 2
lib/osutil/replacingwriter.go

@@ -9,7 +9,8 @@ package osutil
 import (
 	"bytes"
 	"io"
-	"runtime"
+
+	"github.com/syncthing/syncthing/lib/build"
 )
 
 type ReplacingWriter struct {
@@ -51,7 +52,7 @@ func (w ReplacingWriter) Write(bs []byte) (int, error) {
 // LineEndingsWriter returns a writer that writes platform-appropriate line
 // endings. (This is a no-op on non-Windows platforms.)
 func LineEndingsWriter(w io.Writer) io.Writer {
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		return w
 	}
 	return &ReplacingWriter{

+ 3 - 2
lib/osutil/rlimit_unix.go

@@ -10,8 +10,9 @@
 package osutil
 
 import (
-	"runtime"
 	"syscall"
+
+	"github.com/syncthing/syncthing/lib/build"
 )
 
 const (
@@ -36,7 +37,7 @@ func MaximizeOpenFileLimit() (int, error) {
 
 	// macOS doesn't like a soft limit greater then OPEN_MAX
 	// See also: man setrlimit
-	if runtime.GOOS == "darwin" && lim.Max > darwinOpenMax {
+	if build.IsDarwin && lim.Max > darwinOpenMax {
 		lim.Max = darwinOpenMax
 	}
 

+ 3 - 3
lib/osutil/traversessymlink_test.go

@@ -9,9 +9,9 @@ package osutil_test
 import (
 	"os"
 	"path/filepath"
-	"runtime"
 	"testing"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/osutil"
 )
@@ -22,7 +22,7 @@ func TestTraversesSymlink(t *testing.T) {
 	testFs := fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir)
 	testFs.MkdirAll("a/b/c", 0755)
 	if err := fs.DebugSymlinkForTestsOnly(testFs, testFs, filepath.Join("a", "b"), filepath.Join("a", "l")); err != nil {
-		if runtime.GOOS == "windows" {
+		if build.IsWindows {
 			t.Skip("Symlinks aren't working")
 		}
 		t.Fatal(err)
@@ -71,7 +71,7 @@ func TestIssue4875(t *testing.T) {
 	testFs := fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir)
 	testFs.MkdirAll(filepath.Join("a", "b", "c"), 0755)
 	if err := fs.DebugSymlinkForTestsOnly(testFs, testFs, filepath.Join("a", "b"), filepath.Join("a", "l")); err != nil {
-		if runtime.GOOS == "windows" {
+		if build.IsWindows {
 			t.Skip("Symlinks aren't working")
 		}
 		t.Fatal(err)

+ 4 - 6
lib/protocol/bep_extensions.go

@@ -8,9 +8,9 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"runtime"
 	"time"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/rand"
 	"github.com/syncthing/syncthing/lib/sha256"
 )
@@ -286,15 +286,13 @@ func ModTimeEqual(a, b time.Time, modTimeWindow time.Duration) bool {
 }
 
 func PermsEqual(a, b uint32) bool {
-	switch runtime.GOOS {
-	case "windows":
+	if build.IsWindows {
 		// There is only writeable and read only, represented for user, group
 		// and other equally. We only compare against user.
 		return a&0600 == b&0600
-	default:
-		// All bits count
-		return a&0777 == b&0777
 	}
+	// All bits count
+	return a&0777 == b&0777
 }
 
 // BlocksEqual returns true when the two files have identical block lists.

+ 2 - 2
lib/protocol/protocol_test.go

@@ -11,13 +11,13 @@ import (
 	"errors"
 	"io"
 	"os"
-	"runtime"
 	"sync"
 	"testing"
 	"testing/quick"
 	"time"
 
 	lz4 "github.com/pierrec/lz4/v4"
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/rand"
 	"github.com/syncthing/syncthing/lib/testutils"
 )
@@ -848,7 +848,7 @@ func TestIsEquivalent(t *testing.T) {
 		},
 	}
 
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		// On windows we only check the user writable bit of the permission
 		// set, so these are equivalent.
 		cases = append(cases, testCase{

+ 4 - 4
lib/scanner/walk.go

@@ -11,13 +11,13 @@ import (
 	"errors"
 	"fmt"
 	"path/filepath"
-	"runtime"
 	"strings"
 	"sync/atomic"
 	"time"
 	"unicode/utf8"
 
 	metrics "github.com/rcrowley/go-metrics"
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/ignore"
@@ -470,7 +470,7 @@ func (w *walker) walkDir(ctx context.Context, relPath string, info fs.FileInfo,
 func (w *walker) walkSymlink(ctx context.Context, relPath string, info fs.FileInfo, finishedChan chan<- ScanResult) error {
 	// Symlinks are not supported on Windows. We ignore instead of returning
 	// an error.
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		return nil
 	}
 
@@ -519,7 +519,7 @@ func (w *walker) walkSymlink(ctx context.Context, relPath string, info fs.FileIn
 // normalizePath returns the normalized relative path (possibly after fixing
 // it on disk), or skip is true.
 func (w *walker) normalizePath(path string, info fs.FileInfo) (normPath string, err error) {
-	if runtime.GOOS == "darwin" {
+	if build.IsDarwin {
 		// Mac OS X file names should always be NFD normalized.
 		normPath = norm.NFD.String(path)
 	} else {
@@ -579,7 +579,7 @@ func (w *walker) normalizePath(path string, info fs.FileInfo) (normPath string,
 // do not depend on type, and things that should be preserved from the
 // previous version of the FileInfo.
 func (w *walker) updateFileInfo(dst, src protocol.FileInfo) protocol.FileInfo {
-	if dst.Type == protocol.FileInfoTypeFile && runtime.GOOS == "windows" {
+	if dst.Type == protocol.FileInfoTypeFile && build.IsWindows {
 		// If we have an existing index entry, copy the executable bits
 		// from there.
 		dst.Permissions |= (src.Permissions & 0111)

+ 9 - 9
lib/scanner/walk_test.go

@@ -15,13 +15,13 @@ import (
 	"io"
 	"os"
 	"path/filepath"
-	"runtime"
 	rdebug "runtime/debug"
 	"sort"
 	"sync"
 	"testing"
 
 	"github.com/d4l3k/messagediff"
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/ignore"
@@ -178,7 +178,7 @@ func TestVerify(t *testing.T) {
 }
 
 func TestNormalization(t *testing.T) {
-	if runtime.GOOS == "darwin" {
+	if build.IsDarwin {
 		t.Skip("Normalization test not possible on darwin")
 		return
 	}
@@ -197,7 +197,7 @@ func TestNormalization(t *testing.T) {
 	}
 	numInvalid := 2
 
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		// On Windows, in case 5 the character gets replaced with a
 		// replacement character \xEF\xBF\xBD at the point it's written to disk,
 		// which means it suddenly becomes valid (sort of).
@@ -257,7 +257,7 @@ func TestNormalization(t *testing.T) {
 func TestNormalizationDarwinCaseFS(t *testing.T) {
 	// This tests that normalization works on Darwin, through a CaseFS.
 
-	if runtime.GOOS != "darwin" {
+	if !build.IsDarwin {
 		t.Skip("Normalization test not possible on non-Darwin")
 		return
 	}
@@ -321,7 +321,7 @@ func TestIssue1507(_ *testing.T) {
 }
 
 func TestWalkSymlinkUnix(t *testing.T) {
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		t.Skip("skipping unsupported symlink test")
 		return
 	}
@@ -351,7 +351,7 @@ func TestWalkSymlinkUnix(t *testing.T) {
 }
 
 func TestWalkSymlinkWindows(t *testing.T) {
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skip("skipping unsupported symlink test")
 	}
 
@@ -386,7 +386,7 @@ func TestWalkRootSymlink(t *testing.T) {
 	dest, _ := filepath.Abs("testdata/dir1")
 	destFs := fs.NewFilesystem(testFsType, dest)
 	if err := fs.DebugSymlinkForTestsOnly(destFs, testFs, ".", "link"); err != nil {
-		if runtime.GOOS == "windows" {
+		if build.IsWindows {
 			// Probably we require permissions we don't have.
 			t.Skip("Need admin permissions or developer mode to run symlink test on Windows: " + err.Error())
 		} else {
@@ -406,7 +406,7 @@ func TestWalkRootSymlink(t *testing.T) {
 	files = walkDir(testFs, "link", nil, nil, 0)
 
 	// Verify that we got the one symlink, except on windows
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		if len(files) != 0 {
 			t.Errorf("expected no files, not %d", len(files))
 		}
@@ -588,7 +588,7 @@ func TestScanOwnershipPOSIX(t *testing.T) {
 }
 
 func TestScanOwnershipWindows(t *testing.T) {
-	if runtime.GOOS != "windows" {
+	if !build.IsWindows {
 		t.Skip("This test only works on Windows")
 	}
 

+ 6 - 6
lib/upgrade/upgrade_common.go

@@ -15,6 +15,8 @@ import (
 	"runtime"
 	"strconv"
 	"strings"
+
+	"github.com/syncthing/syncthing/lib/build"
 )
 
 type Release struct {
@@ -236,15 +238,13 @@ func releaseNames(tag string) []string {
 	// standard, containing both the architecture/OS and the tag name we
 	// expect. This protects against malformed release data potentially
 	// tricking us into doing a downgrade.
-	switch runtime.GOOS {
-	case "darwin":
+	if build.IsDarwin {
 		return []string{
 			fmt.Sprintf("syncthing-macos-%s-%s.", runtime.GOARCH, tag),
 			fmt.Sprintf("syncthing-macosx-%s-%s.", runtime.GOARCH, tag),
 		}
-	default:
-		return []string{
-			fmt.Sprintf("syncthing-%s-%s-%s.", runtime.GOOS, runtime.GOARCH, tag),
-		}
+	}
+	return []string{
+		fmt.Sprintf("syncthing-%s-%s-%s.", runtime.GOOS, runtime.GOARCH, tag),
 	}
 }

+ 3 - 1
lib/upgrade/upgrade_test.go

@@ -14,6 +14,8 @@ import (
 	"runtime"
 	"strings"
 	"testing"
+
+	"github.com/syncthing/syncthing/lib/build"
 )
 
 var versions = []struct {
@@ -120,7 +122,7 @@ func TestSelectedRelease(t *testing.T) {
 }
 
 func TestSelectedReleaseMacOS(t *testing.T) {
-	if runtime.GOOS != "darwin" {
+	if !build.IsDarwin {
 		t.Skip("macOS only")
 	}
 

+ 2 - 2
lib/upnp/upnp.go

@@ -42,13 +42,13 @@ import (
 	"net"
 	"net/http"
 	"net/url"
-	"runtime"
 	"strings"
 	"sync"
 	"time"
 
 	"github.com/pkg/errors"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/dialer"
 	"github.com/syncthing/syncthing/lib/nat"
 )
@@ -100,7 +100,7 @@ func Discover(ctx context.Context, _, timeout time.Duration) []nat.Device {
 
 	for _, intf := range interfaces {
 		// Interface flags seem to always be 0 on Windows
-		if runtime.GOOS != "windows" && (intf.Flags&net.FlagUp == 0 || intf.Flags&net.FlagMulticast == 0) {
+		if !build.IsWindows && (intf.Flags&net.FlagUp == 0 || intf.Flags&net.FlagMulticast == 0) {
 			continue
 		}
 

+ 2 - 2
lib/versioner/external.go

@@ -12,10 +12,10 @@ import (
 	"fmt"
 	"os"
 	"os/exec"
-	"runtime"
 	"strings"
 	"time"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/fs"
 
@@ -35,7 +35,7 @@ type external struct {
 func newExternal(cfg config.FolderConfiguration) Versioner {
 	command := cfg.Versioning.Params["command"]
 
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		command = strings.ReplaceAll(command, `\`, `\\`)
 	}
 

+ 2 - 2
lib/versioner/external_test.go

@@ -9,9 +9,9 @@ package versioner
 import (
 	"os"
 	"path/filepath"
-	"runtime"
 	"testing"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/fs"
 )
 
@@ -45,7 +45,7 @@ func TestExternalNoCommand(t *testing.T) {
 
 func TestExternal(t *testing.T) {
 	cmd := "./_external_test/external.sh %FOLDER_PATH% %FILE_PATH%"
-	if runtime.GOOS == "windows" {
+	if build.IsWindows {
 		cmd = `.\\_external_test\\external.bat %FOLDER_PATH% %FILE_PATH%`
 	}
 

+ 2 - 2
lib/watchaggregator/aggregator_test.go

@@ -10,11 +10,11 @@ import (
 	"context"
 	"os"
 	"path/filepath"
-	"runtime"
 	"strconv"
 	"testing"
 	"time"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/fs"
@@ -329,7 +329,7 @@ func testAggregatorOutput(t *testing.T, fsWatchChan <-chan []string, expectedBat
 			continue
 		}
 
-		if runtime.GOOS != "darwin" {
+		if !build.IsDarwin {
 			now := time.Since(startTime)
 			if innerIndex == 0 {
 				switch {

+ 3 - 2
test/usage_unix.go

@@ -12,9 +12,10 @@ package integration
 import (
 	"log"
 	"os"
-	"runtime"
 	"syscall"
 	"time"
+
+	"github.com/syncthing/syncthing/lib/build"
 )
 
 func printUsage(name string, proc *os.ProcessState, total int64) {
@@ -22,7 +23,7 @@ func printUsage(name string, proc *os.ProcessState, total int64) {
 		mib := total / 1024 / 1024
 		log.Printf("%s: Utime: %s / MiB", name, time.Duration(rusage.Utime.Nano()/mib))
 		log.Printf("%s: Stime: %s / MiB", name, time.Duration(rusage.Stime.Nano()/mib))
-		if runtime.GOOS == "darwin" {
+		if build.IsDarwin {
 			// Darwin reports in bytes, Linux seems to report in KiB even
 			// though the manpage says otherwise.
 			rusage.Maxrss /= 1024

+ 2 - 1
test/util.go

@@ -25,6 +25,7 @@ import (
 	"time"
 	"unicode"
 
+	"github.com/syncthing/syncthing/lib/build"
 	"github.com/syncthing/syncthing/lib/rc"
 	"github.com/syncthing/syncthing/lib/sha256"
 )
@@ -174,7 +175,7 @@ func alterFiles(dir string) error {
 
 		// Change capitalization
 		case r == 2 && comps > 3 && rand.Float64() < 0.2:
-			if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
+			if build.IsDarwin || build.IsWindows {
 				// Syncthing is currently broken for case-only renames on case-
 				// insensitive platforms.
 				// https://github.com/syncthing/syncthing/issues/1787