mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-09-15 21:00:27 +00:00
237 lines
6.6 KiB
Go
237 lines
6.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"flag"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
|
"github.com/sagernet/sing-box/common/windivert"
|
|
"github.com/sagernet/sing-box/log"
|
|
"github.com/sagernet/sing-usbip/driverassets"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
)
|
|
|
|
var (
|
|
debugEnabled bool
|
|
outputPath string
|
|
target string
|
|
)
|
|
|
|
func init() {
|
|
flag.BoolVar(&debugEnabled, "debug", false, "enable debug")
|
|
flag.StringVar(&outputPath, "output", "", "output path")
|
|
flag.StringVar(&target, "target", runtime.GOOS+"/"+runtime.GOARCH, "target platform")
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
err := build()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func build() error {
|
|
targetParts := strings.Split(target, "/")
|
|
if len(targetParts) != 2 || targetParts[0] == "" || targetParts[1] == "" {
|
|
return E.New("invalid target: ", target)
|
|
}
|
|
operatingSystem := targetParts[0]
|
|
architecture := targetParts[1]
|
|
if outputPath == "" {
|
|
outputPath = "sing-box-daemon"
|
|
if operatingSystem == "windows" {
|
|
outputPath += ".exe"
|
|
}
|
|
}
|
|
absoluteOutputPath, err := filepath.Abs(outputPath)
|
|
if err != nil {
|
|
return E.Cause(err, "resolve output path")
|
|
}
|
|
err = os.MkdirAll(filepath.Dir(absoluteOutputPath), 0o755)
|
|
if err != nil {
|
|
return E.Cause(err, "create output directory")
|
|
}
|
|
version, err := build_shared.ReadTag()
|
|
if err != nil {
|
|
return E.Cause(err, "read version")
|
|
}
|
|
cgoEnabled := operatingSystem != "windows" && os.Getenv("CC") != ""
|
|
tags, err := buildTags(operatingSystem, architecture, cgoEnabled)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
arguments := []string{
|
|
"build",
|
|
"-v",
|
|
"-trimpath",
|
|
"-buildvcs=false",
|
|
"-tags", strings.Join(tags, ","),
|
|
"-ldflags", build_shared.LinkerFlags(version, debugEnabled),
|
|
"-o", absoluteOutputPath,
|
|
}
|
|
if operatingSystem == "windows" && architecture == "386" {
|
|
arguments = append(arguments, "-gcflags=net=-l")
|
|
}
|
|
arguments = append(arguments, "./experimental/boxdd")
|
|
command := exec.Command("go", arguments...)
|
|
cgoEnabledValue := "0"
|
|
if cgoEnabled {
|
|
cgoEnabledValue = "1"
|
|
}
|
|
command.Env = append(os.Environ(),
|
|
"CGO_ENABLED="+cgoEnabledValue,
|
|
"GOOS="+operatingSystem,
|
|
"GOARCH="+architecture,
|
|
"GOTOOLCHAIN=local",
|
|
)
|
|
command.Stdout = os.Stdout
|
|
command.Stderr = os.Stderr
|
|
err = command.Run()
|
|
if err != nil {
|
|
return E.Cause(err, "build sing-box daemon")
|
|
}
|
|
if operatingSystem == "windows" {
|
|
err = stageWinDivertDriver(architecture, filepath.Dir(absoluteOutputPath))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = stageUSBIPDrivers(architecture, filepath.Dir(absoluteOutputPath))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func stageUSBIPDrivers(architecture string, outputDirectory string) error {
|
|
driverPackages := []struct {
|
|
assets map[string]driverassets.Package
|
|
assetDir string
|
|
}{
|
|
{driverassets.VBoxUSB, filepath.Join("internal", "vboxusb", "assets")},
|
|
{driverassets.VHCI, filepath.Join("internal", "usbipvhci", "assets")},
|
|
}
|
|
var moduleDirectory string
|
|
for _, driverPackage := range driverPackages {
|
|
staged := driverPackage.assets[architecture]
|
|
for _, architecturePackage := range driverPackage.assets {
|
|
for _, file := range architecturePackage.Files {
|
|
if slices.ContainsFunc(staged.Files, func(stagedFile driverassets.File) bool {
|
|
return stagedFile.Name == file.Name
|
|
}) {
|
|
continue
|
|
}
|
|
err := os.Remove(filepath.Join(outputDirectory, file.Name))
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return E.Cause(err, "remove stale ", file.Name)
|
|
}
|
|
}
|
|
}
|
|
if len(staged.Files) == 0 {
|
|
continue
|
|
}
|
|
if moduleDirectory == "" {
|
|
listOutput, err := exec.Command("go", "list", "-m", "-f", "{{.Dir}}", "github.com/sagernet/sing-usbip").Output()
|
|
if err != nil {
|
|
return E.Cause(err, "locate sing-usbip module directory")
|
|
}
|
|
moduleDirectory = strings.TrimSpace(string(listOutput))
|
|
}
|
|
for _, file := range staged.Files {
|
|
content, err := os.ReadFile(filepath.Join(moduleDirectory, driverPackage.assetDir, architecture, file.Name))
|
|
if err != nil {
|
|
return E.Cause(err, "read ", file.Name)
|
|
}
|
|
checksum := sha256.Sum256(content)
|
|
if hex.EncodeToString(checksum[:]) != file.SHA256 {
|
|
return E.New(file.Name, " does not match the digest declared in sing-usbip/driverassets")
|
|
}
|
|
targetPath := filepath.Join(outputDirectory, file.Name)
|
|
stagedContent, err := os.ReadFile(targetPath)
|
|
if err == nil && bytes.Equal(stagedContent, content) {
|
|
continue
|
|
}
|
|
err = os.WriteFile(targetPath, content, 0o644)
|
|
if err != nil {
|
|
return E.Cause(err, "write ", file.Name)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func stageWinDivertDriver(architecture string, outputDirectory string) error {
|
|
var assetName, assetDigest string
|
|
switch architecture {
|
|
case "amd64":
|
|
assetName, assetDigest = windivert.Asset64Name, windivert.Asset64SHA256
|
|
case "386":
|
|
assetName, assetDigest = windivert.Asset32Name, windivert.Asset32SHA256
|
|
}
|
|
for _, name := range []string{windivert.Asset64Name, windivert.Asset32Name} {
|
|
if name == assetName {
|
|
continue
|
|
}
|
|
err := os.Remove(filepath.Join(outputDirectory, name))
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return E.Cause(err, "remove stale ", name)
|
|
}
|
|
}
|
|
if assetName == "" {
|
|
return nil
|
|
}
|
|
assetDirectory := filepath.Join("common", "windivert", "assets")
|
|
content, err := os.ReadFile(filepath.Join(assetDirectory, assetName))
|
|
if err != nil {
|
|
return E.Cause(err, "read ", assetName)
|
|
}
|
|
checksum := sha256.Sum256(content)
|
|
if hex.EncodeToString(checksum[:]) != assetDigest {
|
|
return E.New(assetName, " does not match the digest declared in common/windivert")
|
|
}
|
|
targetPath := filepath.Join(outputDirectory, assetName)
|
|
staged, err := os.ReadFile(targetPath)
|
|
if err == nil && bytes.Equal(staged, content) {
|
|
return nil
|
|
}
|
|
err = os.WriteFile(targetPath, content, 0o644)
|
|
if err != nil {
|
|
return E.Cause(err, "write ", assetName)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func buildTags(operatingSystem string, architecture string, cgoEnabled bool) ([]string, error) {
|
|
tagsFile := "release/DEFAULT_BUILD_TAGS"
|
|
if operatingSystem == "windows" {
|
|
if architecture == "386" {
|
|
tagsFile = "release/DEFAULT_BUILD_TAGS_OTHERS"
|
|
} else {
|
|
tagsFile = "release/DEFAULT_BUILD_TAGS_WINDOWS"
|
|
}
|
|
} else if !cgoEnabled {
|
|
tagsFile = "release/DEFAULT_BUILD_TAGS_OTHERS"
|
|
}
|
|
content, err := os.ReadFile(tagsFile)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "read build tags")
|
|
}
|
|
tags := strings.Split(strings.TrimSpace(string(content)), ",")
|
|
if operatingSystem == "windows" {
|
|
tags = append(tags, "with_external_windivert", "with_external_usbip_drivers")
|
|
}
|
|
if debugEnabled {
|
|
tags = append(tags, "debug")
|
|
}
|
|
return tags, nil
|
|
}
|