Files
Archive/yass/tools/build.go
2024-11-29 19:38:36 +01:00

2649 lines
90 KiB
Go

package main
import (
"archive/zip"
"bufio"
"bytes"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"encoding/hex"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"reflect"
"regexp"
"runtime"
"strconv"
"strings"
"github.com/golang/glog"
"github.com/google/uuid"
)
var APPNAME = "yass"
var toolDir string
var projectDir string
var buildDir string
var tagFlag string
var subtagFlag string
var refTag string
var fullTagFlag string
var dryRunFlag bool
var preCleanFlag bool
var noBuildFlag bool
var noConfigureFlag bool
var noPackagingFlag bool
var buildBenchmarkFlag bool
var runBenchmarkFlag bool
var buildTestFlag bool
var runTestFlag bool
var verboseFlag int
var cmakeBuildTypeFlag string
var cmakeBuildConcurrencyFlag int
var clangPath string
var useLibCxxFlag bool
var enableLtoFlag bool
var useMoldFlag bool
var useStaticBuildFlag bool
var clangTidyModeFlag bool
var macosxVersionMinFlag string
var macosxUniversalBuildFlag bool
var macosxKeychainPathFlag string
var macosxCodeSignIdentityFlag string
var iosVersionMinFlag string
var iosCodeSignIdentityFlag string
var iosDevelopmentTeamFlag string
var iosTestDeviceNameFlag string
var msvcTargetArchFlag string
var msvcCrtLinkageFlag string
var msvcAllowXpFlag bool
var freebsdAbiFlag int
var systemNameFlag string
var subSystemNameFlag string
var sysrootFlag string
var archFlag string
var armCpuFlag string
var variantFlag string
var mingwDir string
var mingwAllowXpFlag bool
var androidApiLevel int
var androidAab bool
var androidSdkDir string
var androidNdkVer string
var harmonyNdkDir string
func getAppName() string {
if systemNameFlag == "windows" {
return APPNAME + ".exe"
} else if systemNameFlag == "darwin" || systemNameFlag == "ios" {
return APPNAME + ".app"
} else if systemNameFlag == "mingw" {
return APPNAME + ".exe"
} else if systemNameFlag == "android" {
if APPNAME == "yass" {
return "lib" + APPNAME + ".so"
}
return APPNAME
} else if systemNameFlag == "harmony" {
if APPNAME == "yass" {
return "lib" + APPNAME + ".so"
}
return APPNAME
} else {
return APPNAME
}
}
func getEnv(key string, fallback string) string {
if value, ok := os.LookupEnv(key); ok {
return value
}
return fallback
}
func getEnvBool(key string, fallback bool) bool {
if value, ok := os.LookupEnv(key); ok {
result, _ := strconv.ParseBool(value)
return result
}
return fallback
}
func InitFlag() {
_, filename, _, ok := runtime.Caller(1)
if !ok {
glog.Fatalf("source code not found")
}
toolDir = filepath.Dir(filename)
projectDir = filepath.Dir(toolDir)
// override defaults
flag.Set("v", "2")
flag.Set("alsologtostderr", "true")
var flagNoPreClean bool
flag.BoolVar(&dryRunFlag, "dry-run", false, "Generate build script but without actually running it")
flag.BoolVar(&preCleanFlag, "pre-clean", true, "Clean the source tree before building")
flag.BoolVar(&flagNoPreClean, "nc", false, "Don't Clean the source tree before building")
flag.BoolVar(&noBuildFlag, "no-build", false, "Skip build step")
flag.BoolVar(&noConfigureFlag, "no-configure", false, "Skip configure step")
flag.BoolVar(&noPackagingFlag, "no-packaging", false, "Skip packaging step")
flag.BoolVar(&buildBenchmarkFlag, "build-benchmark", false, "Build benchmarks")
flag.BoolVar(&runBenchmarkFlag, "run-benchmark", false, "Build and run benchmarks")
flag.BoolVar(&buildTestFlag, "build-test", false, "Build unittests")
flag.BoolVar(&runTestFlag, "run-test", false, "Build and run unittests")
flag.IntVar(&verboseFlag, "verbose", 0, "Run unittests with verbose level")
flag.StringVar(&cmakeBuildTypeFlag, "cmake-build-type", getEnv("BUILD_TYPE", "Release"), "Set cmake build configuration")
flag.IntVar(&cmakeBuildConcurrencyFlag, "cmake-build-concurrency", runtime.NumCPU(), "Set cmake build concurrency")
builtClangPath, _ := filepath.Abs(filepath.Join(projectDir, "third_party", "llvm-build", "Release+Asserts"))
flag.StringVar(&clangPath, "clang-path", builtClangPath, "Compiler clang's path")
flag.BoolVar(&useStaticBuildFlag, "use-static-build", false, "Use Static Build for CLI and Server")
flag.BoolVar(&useLibCxxFlag, "use-libcxx", true, "Use Custom libc++")
flag.BoolVar(&enableLtoFlag, "enable-lto", true, "Enable lto")
flag.BoolVar(&useMoldFlag, "use-mold", false, "Use Mold Linker")
flag.BoolVar(&clangTidyModeFlag, "clang-tidy-mode", getEnvBool("ENABLE_CLANG_TIDY", false), "Enable Clang Tidy Build")
flag.StringVar(&macosxVersionMinFlag, "macosx-version-min", getEnv("MACOSX_DEPLOYMENT_TARGET", "10.14"), "Set Mac OS X deployment target, such as 10.15")
flag.BoolVar(&macosxUniversalBuildFlag, "macosx-universal-build", getEnvBool("ENABLE_OSX_UNIVERSAL_BUILD", false), "Enable Mac OS X Universal Build")
flag.StringVar(&macosxKeychainPathFlag, "macosx-keychain-path", getEnv("KEYCHAIN_PATH", ""), "During signing, only search for the signing identity in the keychain file specified")
flag.StringVar(&macosxCodeSignIdentityFlag, "macosx-codesign-identity", getEnv("CODESIGN_IDENTITY", "-"), "Set Mac OS X CodeSign Identity")
flag.StringVar(&iosVersionMinFlag, "ios-version-min", getEnv("MACOSX_DEPLOYMENT_TARGET", "13.0"), "Set iOS deployment target, such as 13.0")
flag.StringVar(&iosCodeSignIdentityFlag, "ios-codesign-identity", getEnv("CODESIGN_IDENTITY", "-"), "Set iOS CodeSign Identity")
flag.StringVar(&iosDevelopmentTeamFlag, "ios-development-team", getEnv("DEVELOPMENT_TEAM", ""), "Set iOS deployment team")
flag.StringVar(&iosTestDeviceNameFlag, "ios-test-device-name", getEnv("IPHONE_NAME", ""), "Set iOS test device name")
flag.StringVar(&msvcTargetArchFlag, "msvc-tgt-arch", getEnv("VSCMD_ARG_TGT_ARCH", "x64"), "Set Visual C++ Target Achitecture")
flag.StringVar(&msvcCrtLinkageFlag, "msvc-crt-linkage", getEnv("MSVC_CRT_LINKAGE", "dynamic"), "Set Visual C++ CRT Linkage")
flag.BoolVar(&msvcAllowXpFlag, "msvc-allow-xp", getEnvBool("MSVC_ALLOW_XP", false), "Enable Windows XP Build")
flag.IntVar(&freebsdAbiFlag, "freebsd-abi", getFreebsdABI(11), "Select FreeBSD ABI")
flag.StringVar(&systemNameFlag, "system", runtime.GOOS, "Specify host system name")
flag.StringVar(&subSystemNameFlag, "subsystem", "", "Specify host subsystem name")
flag.StringVar(&sysrootFlag, "sysroot", "", "Specify host sysroot, used in cross-compiling")
flag.StringVar(&archFlag, "arch", runtime.GOARCH, "Specify host architecture")
flag.StringVar(&armCpuFlag, "arm-cpu", "", "Specify ARM CPU Model Name if any")
flag.StringVar(&variantFlag, "variant", "gui", "Specify variant, available: gui, cli, server")
flag.StringVar(&mingwDir, "mingw-dir", "", "MinGW Dir Path")
flag.BoolVar(&mingwAllowXpFlag, "mingw-allow-xp", false, "Enable Windows XP Build")
flag.BoolVar(&androidAab, "android-aab", false, "Build aab format instead of apk")
flag.IntVar(&androidApiLevel, "android-api", 24, "Select Android API Level")
flag.StringVar(&androidSdkDir, "android-sdk-dir", getEnv("ANDROID_SDK_ROOT", ""), "Android SDK Home Path")
flag.StringVar(&androidNdkVer, "android-ndk-ver", getEnv("ANDROID_NDK_VER", "26.3.11579264"), "Android NDK Version")
flag.StringVar(&harmonyNdkDir, "harmony-ndk-dir", getEnv("HARMONY_NDK_ROOT", ""), "OpenHarmony NDK (toolchain) Path")
flag.Parse()
if flagNoPreClean {
preCleanFlag = false
}
// clang-tidy complains about parse error
if clangTidyModeFlag {
macosxUniversalBuildFlag = false
noPackagingFlag = true
}
// For compatiblity
systemNameFlag = strings.ToLower(systemNameFlag)
if variantFlag == "gui" {
APPNAME = "yass"
} else if variantFlag == "cli" {
APPNAME = "yass_cli"
} else if variantFlag == "server" {
APPNAME = "yass_server"
} else if variantFlag == "benchmark" {
APPNAME = "yass_benchmark"
} else if variantFlag == "test" {
APPNAME = "yass_test"
} else {
glog.Fatalf("Invalid variant: %s", variantFlag)
}
}
func prebuildFindSourceDirectory() {
glog.Info("PreStage -- Find Source Directory")
glog.Info("======================================================================")
err := os.Chdir(projectDir)
if err != nil {
glog.Fatalf("%v", err)
}
if _, err = os.Stat("CMakeLists.txt"); errors.Is(err, os.ErrNotExist) {
glog.Fatalf("Cannot find top dir of the source tree")
}
// pretend PATH
if runtime.GOOS == "windows" {
path := os.Getenv("PATH")
path = "C:\\Program Files (x86)\\NASM;" + path
path = "C:\\Program Files\\NASM;" + path
path = "C:\\Program Files (x86)\\WiX Toolset v3.14\\bin;" + path
path = "C:\\Program Files\\CMake\\bin;" + path
path = "C:\\Program Files (x86)\\NSIS;" + path
path = "C:\\Program Files (x86)\\7-Zip;" + path
path = "C:\\Program Files\\7-Zip;" + path
path = "C:\\ProgramData\\chocolatey\\bin;" + path
path = filepath.Join(projectDir, "third_party", "nasm") + ";" + path
path = filepath.Join(projectDir, "third_party", "wix311") + ";" + path
path = filepath.Join(projectDir, "third_party", "nsis") + ";" + path
os.Setenv("PATH", path)
}
if _, err = os.Stat(".git"); err == nil {
cmd := exec.Command("git", "describe", "--abbrev=0", "--tags", "HEAD")
var outb, errb bytes.Buffer
cmd.Stdout = &outb
cmd.Stderr = &errb
err = cmd.Run()
if err == nil {
tagFlag = strings.TrimSpace(outb.String())
}
}
if err != nil {
tagContent, err := ioutil.ReadFile("TAG")
if err != nil {
glog.Fatalf("%v", err)
}
tagFlag = strings.TrimSpace(string(tagContent))
}
if _, err = os.Stat(".git"); err == nil {
cmd := exec.Command("git", "rev-list", fmt.Sprintf("%s..HEAD", tagFlag), "--count")
var outb, errb bytes.Buffer
cmd.Stdout = &outb
cmd.Stderr = &errb
err = cmd.Run()
if err == nil {
subtagFlag = strings.TrimSpace(outb.String())
}
}
if err != nil {
var subtagContent []byte
subtagContent, err = ioutil.ReadFile("SUBTAG")
if err != nil {
glog.Fatalf("%v", err)
}
subtagFlag = strings.TrimSpace(string(subtagContent))
}
if _, err = os.Stat(".git"); err == nil {
cmd := exec.Command("git", "rev-parse", "HEAD")
var outb, errb bytes.Buffer
cmd.Stdout = &outb
cmd.Stderr = &errb
err = cmd.Run()
if err == nil {
refTag = strings.TrimSpace(outb.String())[:8]
}
}
if err != nil {
var lastChangeContent []byte
lastChangeContent, err = ioutil.ReadFile("LAST_CHANGE")
if err != nil {
glog.Fatalf("%v", err)
}
refTag = strings.TrimSpace(string(lastChangeContent))[:8]
}
if subtagFlag == "0" {
fullTagFlag = tagFlag
} else {
fullTagFlag = fmt.Sprintf("%s-%s-%s", tagFlag, subtagFlag, refTag)
}
if systemNameFlag == "windows" && msvcTargetArchFlag != "" {
osSuffix := ""
if msvcAllowXpFlag {
osSuffix = "-winxp"
}
buildDir = fmt.Sprintf("build-msvc%s-%s-%s", osSuffix, msvcTargetArchFlag, msvcCrtLinkageFlag)
} else if systemNameFlag == "mingw" {
osSuffix := ""
if mingwAllowXpFlag {
osSuffix = "-winxp"
}
buildDir = fmt.Sprintf("build-%s%s-%s", systemNameFlag, osSuffix, archFlag)
} else if systemNameFlag == "freebsd" {
buildDir = fmt.Sprintf("build-%s%d-%s", systemNameFlag, freebsdAbiFlag, archFlag)
} else if systemNameFlag == "android" {
buildDir = fmt.Sprintf("build-%s%d-%s", systemNameFlag, androidApiLevel, archFlag)
} else if systemNameFlag == "ios" {
buildDir = fmt.Sprintf("build-%s-%s", systemNameFlag, archFlag)
if subSystemNameFlag != "" {
buildDir = fmt.Sprintf("build-%s-%s-%s", systemNameFlag, subSystemNameFlag, archFlag)
}
} else {
arch := archFlag
if systemNameFlag == "darwin" && macosxUniversalBuildFlag {
arch = "universal"
}
buildDir = fmt.Sprintf("build-%s-%s", systemNameFlag, arch)
if subSystemNameFlag != "" {
buildDir = fmt.Sprintf("build-%s-%s-%s", systemNameFlag, subSystemNameFlag, arch)
}
}
buildDir, err = filepath.Abs(buildDir)
if err != nil {
glog.Fatalf("%v", err)
}
glog.V(2).Infof("Set build directory %s", buildDir)
if _, err := os.Stat(buildDir); err == nil {
if preCleanFlag {
err := os.RemoveAll(buildDir)
if err != nil {
glog.Fatalf("%v", err)
}
glog.V(2).Infof("Removed previous build directory %s", buildDir)
}
}
if _, err := os.Stat(buildDir); errors.Is(err, os.ErrNotExist) {
err := os.Mkdir(buildDir, 0777)
if err != nil {
glog.Fatalf("%v", err)
}
}
glog.V(1).Infof("Changed to build directory %s", buildDir)
err = os.Chdir(buildDir)
if err != nil {
glog.Fatalf("%v", err)
}
}
func getLLVMTargetTripleMSVC(msvcTargetArch string) string {
if msvcTargetArch == "x86" {
return "i686-pc-windows-msvc"
} else if msvcTargetArch == "x64" {
return "x86_64-pc-windows-msvc"
} else if msvcTargetArch == "arm" {
// lld-link doesn't accept this triple
// cmake_args.extend(["-DCMAKE_LINKER=link"])
return "arm-pc-windows-msvc"
} else if msvcTargetArch == "arm64" {
return "arm64-pc-windows-msvc"
}
glog.Fatalf("Invalid msvcTargetArch: %s", msvcTargetArch)
return ""
}
func getMinGWTargetAndAppAbi(arch string) (string, string) {
if arch == "x86" || arch == "i686" {
return "i686-w64-mingw32", "i686"
} else if arch == "x64" || arch == "x86_64" || arch == "amd64" {
return "x86_64-w64-mingw32", "x86_64"
} else if arch == "arm64" || arch == "aarch64" {
return "aarch64-w64-mingw32", "aarch64"
}
glog.Fatalf("Invalid MinGW arch: %s", arch)
return "", ""
}
// build/config/android/abi.gni
func getAndroidTargetAndAppAbi(arch string) (string, string) {
if arch == "x64" {
return "x86_64-linux-android", "x86_64"
} else if arch == "x86" {
return "i686-linux-android", "x86"
} else if arch == "arm64" {
return "aarch64-linux-android", "arm64-v8a"
} else if arch == "arm" {
// armeabi for armv6
return "arm-linux-androideabi", "armeabi-v7a"
} else if arch == "mipsel" {
return "mipsel-linux-android", "mips"
} else if arch == "mips64el" {
return "mips64el-linux-android", "mips64"
} else if arch == "riscv64" {
return "riscv64-linux-android", "riscv64"
}
glog.Fatalf("Invalid arch: %s", arch)
return "", ""
}
// ohos.toolchain.cmake
func getHarmonyTargetAndAppAbi(arch string) (string, string) {
if arch == "x64" {
return "x86_64-linux-ohos", "x86_64"
} else if arch == "arm64" {
return "aarch64-linux-ohos", "arm64-v8a"
} else if arch == "arm" {
// armeabi for armv6
return "arm-linux-ohos", "armeabi-v7a"
}
glog.Fatalf("Invalid arch: %s", arch)
return "", ""
}
// https://docs.rust-embedded.org/embedonomicon/compiler-support.html
func getGNUTargetTypeAndArch(arch string, subsystem string) (string, string) {
if arch == "amd64" || arch == "x86_64" {
if subsystem == "musl" {
return "x86_64-linux-musl", "x86_64"
}
return "x86_64-linux-gnu", "x86_64"
} else if arch == "x86" {
if subsystem == "musl" {
return "i686-linux-musl", "i686"
}
return "i686-linux-gnu", "i686"
} else if arch == "i386" || arch == "i486" || arch == "i586" || arch == "i686" {
if subsystem == "musl" {
return fmt.Sprintf("%s-linux-musl", arch), arch
}
return fmt.Sprintf("%s-linux-gnu", arch), arch
} else if arch == "arm64" || arch == "aarch64" {
if subsystem == "musl" {
return "aarch64-linux-musl", "aarch64"
}
return "aarch64-linux-gnu", "aarch64"
} else if arch == "armel" {
if subsystem == "musl" {
return "arm-linux-musleabi", "armel"
}
return "arm-linux-gnueabi", "armel"
} else if arch == "arm" || arch == "armhf" {
if subsystem == "musl" {
return "arm-linux-musleabihf", "armhf"
}
return "arm-linux-gnueabihf", "armhf"
} else if arch == "mips" {
if subsystem == "musl" {
return "mips-linux-musl", "mips"
}
return "mips-linux-gnu", "mips"
} else if arch == "mips64" {
if subsystem == "musl" {
return "mips64-linux-muslabi64", "mips64"
}
return "mips64-linux-gnuabi64", "mips64"
} else if arch == "mipsel" {
if subsystem == "musl" {
return "mipsel-linux-musl", "mipsel"
}
return "mipsel-linux-gnu", "mipsel"
} else if arch == "mips64el" {
if subsystem == "musl" {
return "mips64el-linux-muslabi64", "mips64el"
}
return "mips64el-linux-gnuabi64", "mips64el"
} else if arch == "loongarch64" {
return "loongarch64-linux-gnu", "loongarch64"
} else if arch == "riscv64" {
return "riscv64-linux-gnu", "riscv64"
} else if arch == "riscv32" {
return "riscv32-linux-gnu", "riscv32"
}
glog.Fatalf("Invalid arch: %s", arch)
return "", ""
}
func getClangVersion(llvmBaseDir string) string {
entries, err := ioutil.ReadDir(filepath.Join(llvmBaseDir, "lib", "clang"))
if err != nil {
glog.Fatalf("%v", err)
}
var llvm_version string
for _, entry := range entries {
llvm_version = entry.Name()
}
if llvm_version == "" {
glog.Fatalf("toolchain not found under directory %s", llvmBaseDir)
}
return llvm_version
}
func getAndFixMinGWLibunwind(mingwDir string) {
getAndFixLibunwind(fmt.Sprintf("%s/lib/clang/%s/lib/windows", mingwDir, getClangVersion(mingwDir)), "windows")
}
func getAndFixAndroidLibunwind(ndkDir string) {
llvmBaseDir := fmt.Sprintf("%s/toolchains/llvm/prebuilt/%s-x86_64", ndkDir, runtime.GOOS)
getAndFixLibunwind(fmt.Sprintf("%s/lib/clang/%s/lib/linux", llvmBaseDir, getClangVersion(llvmBaseDir)), "linux")
}
func getAndFixHarmonyLibunwind() {
// Fix Runtime
llvmBaseDir := fmt.Sprintf("%s/native/llvm", harmonyNdkDir)
getAndFixLibunwind(fmt.Sprintf("%s/lib/clang/%s/lib", llvmBaseDir, getClangVersion(llvmBaseDir)), "")
// Fix libunwind
target_path := fmt.Sprintf(filepath.Join(clangPath, "lib"))
source_path := fmt.Sprintf("%s/native/llvm/lib", harmonyNdkDir)
entries, err := ioutil.ReadDir(source_path)
if err != nil {
glog.Fatalf("%v", err)
}
for _, entry := range entries {
if !strings.HasSuffix(entry.Name(), "-ohos") {
continue
}
if _, err = os.Lstat(filepath.Join(target_path, entry.Name())); err == nil {
err = os.Remove(filepath.Join(target_path, entry.Name()))
if err != nil {
glog.Fatalf("%v", err)
}
}
err = os.Symlink(filepath.Join(source_path, entry.Name()),
filepath.Join(target_path, entry.Name()))
if err != nil {
glog.Fatalf("%v", err)
}
glog.Info("Created symbolic links at ", filepath.Join(target_path, entry.Name()))
}
}
func getAndFixLibunwind(source_path string, subdir string) {
if runtime.GOOS == "windows" {
glog.Fatalf("Symbolic link is not supported on windows")
}
// ln -sf $PWD/third_party/android_toolchain/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/14.0.7/lib/linux/i386 third_party/llvm-build/Release+Asserts/lib/clang/18/lib/linux
target_path := fmt.Sprintf(filepath.Join(clangPath, "lib", "clang", getClangVersion(clangPath), "lib", subdir))
entries, err := ioutil.ReadDir(source_path)
if err != nil {
glog.Fatalf("%v", err)
}
if _, err = os.Stat(target_path); errors.Is(err, os.ErrNotExist) {
err = os.Mkdir(target_path, 0777)
if err != nil {
glog.Fatalf("%v", err)
}
}
for _, entry := range entries {
if subdir == "" && !strings.HasSuffix(entry.Name(), "-ohos") {
continue
}
if _, err = os.Lstat(filepath.Join(target_path, entry.Name())); err == nil {
err = os.Remove(filepath.Join(target_path, entry.Name()))
if err != nil {
glog.Fatalf("%v", err)
}
}
err = os.Symlink(filepath.Join(source_path, entry.Name()),
filepath.Join(target_path, entry.Name()))
if err != nil {
glog.Fatalf("%v", err)
}
glog.Info("Created symbolic links at ", filepath.Join(target_path, entry.Name()))
}
}
func cmdStdinPipe(c *exec.Cmd) (*os.File, *os.File, error) {
if c.Stdin != nil {
return nil, nil, errors.New("exec: Stdin already set")
}
if c.Process != nil {
return nil, nil, errors.New("exec: cmdStdinPipe after process started")
}
pr, pw, err := os.Pipe()
if err != nil {
return nil, nil, err
}
c.Stdin = pr
return pw, pr, nil
}
func cmdStdoutStderrPipe(c *exec.Cmd) (*os.File, *os.File, error) {
if c.Stdout != nil {
return nil, nil, errors.New("exec: Stdout already set")
}
if c.Stderr != nil {
return nil, nil, errors.New("exec: Stderr already set")
}
if c.Process != nil {
return nil, nil, errors.New("exec: cmdStdoutStderrPipe after process started")
}
pr, pw, err := os.Pipe()
if err != nil {
return nil, nil, err
}
c.Stdout = pw
c.Stderr = pw
return pr, pw, nil
}
func cmdRun(args []string, check bool) {
var cmd = exec.Command(args[0], args[1:]...)
glog.V(2).Infof("Running command \"%v\"", args)
if dryRunFlag {
return
}
var stdin, stdinPipe, stdouterr, stdouterrPipe *os.File
var err error
stdin, stdinPipe, err = cmdStdinPipe(cmd)
if err != nil {
if !check {
return
}
glog.Fatalf("%v", err)
}
stdouterr, stdouterrPipe, err = cmdStdoutStderrPipe(cmd)
if err != nil {
if !check {
return
}
glog.Fatalf("%v", err)
}
err = cmd.Start()
err = stdin.Close()
if err != nil {
if !check {
return
}
glog.Fatalf("%v", err)
}
err = stdinPipe.Close()
if err != nil {
if !check {
return
}
glog.Fatalf("%v", err)
}
err = stdouterrPipe.Close()
if err != nil {
if !check {
return
}
glog.Fatalf("%v", err)
}
if err != nil {
glog.Warningf("Command \"%v\" failed with %v", args, err)
if !check {
return
}
glog.Fatalf("%v", err)
}
scanner := bufio.NewScanner(stdouterr)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
glog.Info(scanner.Text())
}
err = cmd.Wait()
if err != nil {
glog.Warningf("Command \"%v\" failed with %v", args, err)
if !check {
return
}
glog.Fatalf("%v", err)
}
err = stdouterr.Close()
if err != nil {
if !check {
return
}
glog.Fatalf("%v", err)
}
}
func cmdCheckOutput(args []string) string {
var cmd = exec.Command(args[0], args[1:]...)
glog.V(2).Infof("Running command \"%v\"", args)
output, err := cmd.Output()
if err != nil {
glog.Fatalf("%v", err)
}
return string(output)
}
func buildStageGenerateBuildScript() {
glog.Info("BuildStage -- Generate Build Script")
glog.Info("======================================================================")
var cmakeArgs []string
if os.Getenv("CC") != "" {
glog.Infof("Using overrided compiler %s", os.Getenv("CC"))
} else if systemNameFlag == "ios" {
glog.Infof("Using xcode's builtin compiler")
} else if clangPath != "" {
if systemNameFlag == "windows" {
_clangClPath := filepath.Join(clangPath, "bin", "clang-cl.exe")
if _, err := os.Stat(_clangClPath); errors.Is(err, os.ErrNotExist) {
glog.Fatalf("Compiler %s not found", _clangClPath)
}
os.Setenv("CC", _clangClPath)
os.Setenv("CXX", _clangClPath)
glog.Infof("Using compiler %s", _clangClPath)
} else if runtime.GOOS == "windows" {
_clangPath := filepath.Join(clangPath, "bin", "clang.exe")
_clangCxxPath := filepath.Join(clangPath, "bin", "clang++.exe")
if _, err := os.Stat(_clangPath); errors.Is(err, os.ErrNotExist) {
glog.Fatalf("Compiler %s not found", _clangPath)
}
os.Setenv("CC", _clangPath)
os.Setenv("CXX", _clangCxxPath)
glog.Infof("Using compiler %s", _clangPath)
} else {
_clangPath := filepath.Join(clangPath, "bin", "clang")
_clangCxxPath := filepath.Join(clangPath, "bin", "clang++")
if _, err := os.Stat(_clangPath); errors.Is(err, os.ErrNotExist) {
glog.Fatalf("Compiler %s not found", _clangPath)
}
os.Setenv("CC", _clangPath)
os.Setenv("CXX", _clangCxxPath)
glog.Infof("Using compiler %s", _clangPath)
}
}
if cmakeBuildTypeFlag == "Release" || cmakeBuildTypeFlag == "MinSizeRel" {
cmakeArgs = append(cmakeArgs, "-DOFFICIAL_BUILD=on")
}
cmakeArgs = append(cmakeArgs, "-DENABLE_LLD=on")
cmakeArgs = append(cmakeArgs, "-DUSE_ZLIB=on")
cmakeArgs = append(cmakeArgs, "-DUSE_JSONCPP=on")
cmakeArgs = append(cmakeArgs, "-DGUI=ON", "-DCLI=ON", "-DSERVER=ON")
if useStaticBuildFlag {
cmakeArgs = append(cmakeArgs, "-DCLI_STATIC_BUILD=ON", "-DSERVER_STATIC_BUILD=ON")
}
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DCMAKE_BUILD_TYPE=%s", cmakeBuildTypeFlag))
if systemNameFlag == "ios" {
cmakeArgs = append(cmakeArgs, "-G", "Xcode")
} else {
cmakeArgs = append(cmakeArgs, "-G", "Ninja")
}
if systemNameFlag != runtime.GOOS || sysrootFlag != "" || msvcTargetArchFlag != "x64" {
cmakeArgs = append(cmakeArgs, "-DUSE_HOST_TOOLS=on")
}
if subSystemNameFlag == "musl" || subSystemNameFlag == "openwrt" {
cmakeArgs = append(cmakeArgs, "-DUSE_MUSL=on")
}
if buildBenchmarkFlag || runBenchmarkFlag {
cmakeArgs = append(cmakeArgs, "-DBUILD_BENCHMARKS=on")
}
if buildTestFlag || runTestFlag {
cmakeArgs = append(cmakeArgs, "-DBUILD_TESTS=on")
}
if useLibCxxFlag {
cmakeArgs = append(cmakeArgs, "-DUSE_LIBCXX=on")
} else {
cmakeArgs = append(cmakeArgs, "-DUSE_LIBCXX=off")
}
if enableLtoFlag {
cmakeArgs = append(cmakeArgs, "-DENABLE_LTO=on")
} else {
cmakeArgs = append(cmakeArgs, "-DENABLE_LTO=off")
}
if useMoldFlag {
cmakeArgs = append(cmakeArgs, "-DUSE_MOLD=on")
} else {
cmakeArgs = append(cmakeArgs, "-DUSE_MOLD=off")
}
if clangTidyModeFlag {
if clangPath == "" {
glog.Fatalf("clangPath is required by clang-tidy mode but not specified")
}
clangTidyPath := filepath.Join(clangPath, "bin", "clang-tidy")
if runtime.GOOS == "windows" {
clangTidyPath = filepath.Join(clangPath, "bin", "clang-tidy.exe")
}
if _, err := os.Stat(clangTidyPath); errors.Is(err, os.ErrNotExist) {
glog.Fatalf("clang-tidy executable %s does not exist", clangTidyPath)
}
cmakeArgs = append(cmakeArgs, "-DENABLE_CLANG_TIDY=on", fmt.Sprintf("-DCLANG_TIDY_EXECUTABLE=%s", clangTidyPath))
}
if systemNameFlag == "windows" {
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DCROSS_TOOLCHAIN_FLAGS_TOOLCHAIN_FILE=%s\\Native.cmake", buildDir))
// Guesting native LIB paths from host LIB paths
var nativeLibPaths []string
hostLibPaths := strings.Split(os.Getenv("LIB"), ";")
for _, hostLibPath := range hostLibPaths {
var nativeLibPath string
if strings.Contains(strings.ToLower(hostLibPath), "v7.1a") {
// Old Windows SDK looks like:
// C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib
// C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib\x64
if strings.HasSuffix(strings.ToLower(hostLibPath), "lib") {
nativeLibPath = filepath.Join(hostLibPath, "x64")
} else {
nativeLibPath = hostLibPath
}
} else {
nativeLibPath = filepath.Join(filepath.Dir(hostLibPath), "x64")
}
nativeLibPaths = append(nativeLibPaths, strings.Replace(nativeLibPath, "\\", "/", -1))
}
nativeLIB := strings.Join(nativeLibPaths, ";")
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DCROSS_TOOLCHAIN_FLAGS_NATIVE_LIB=%s", nativeLIB))
useMsvcCl := getEnv("CC", "cl") == "cl"
ccCompiler := getEnv("CC", "cl")
cxxCompiler := getEnv("CXX", "cl")
// Retarget cl based on current path
if useMsvcCl {
path, err := exec.LookPath(ccCompiler)
if err != nil {
fmt.Println("Could not find path of cl")
glog.Fatalf("%v", err)
}
ccCompiler = filepath.Join(filepath.Dir(path), "..", "x64", "cl.exe")
cxxCompiler = filepath.Join(filepath.Dir(path), "..", "x64", "cl.exe")
}
nativeToolChainContent := strings.Replace(fmt.Sprintf("set(CMAKE_C_COMPILER \"%s\")\n", ccCompiler), "\\", "/", -1)
nativeToolChainContent += strings.Replace(fmt.Sprintf("set(CMAKE_CXX_COMPILER \"%s\")\n", cxxCompiler), "\\", "/", -1)
nativeToolChainContent += "set(CMAKE_C_COMPILER_TARGET \"x86_64-pc-windows-msvc\")\n"
nativeToolChainContent += "set(CMAKE_CXX_COMPILER_TARGET \"x86_64-pc-windows-msvc\")\n"
err := ioutil.WriteFile("Native.cmake", []byte(nativeToolChainContent), 0666)
if err != nil {
glog.Fatalf("%v", err)
}
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DUSE_CARES=%s", "ON"))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DMSVC_CRT_LINKAGE=%s", msvcCrtLinkageFlag))
if msvcAllowXpFlag {
cmakeArgs = append(cmakeArgs, "-DALLOW_XP=ON")
} else {
cmakeArgs = append(cmakeArgs, "-DALLOW_XP=OFF")
}
// Some compilers are inherently cross-compilers, such as Clang and the QNX QCC compiler.
// The CMAKE_<LANG>_COMPILER_TARGET can be set to pass a value to those supported compilers when compiling.
// see https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_TARGET.html
targetTriple := getLLVMTargetTripleMSVC(msvcTargetArchFlag)
if strings.Contains(os.Getenv("CC"), "clang-cl") {
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DCMAKE_C_COMPILER_TARGET=%s", targetTriple))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DCMAKE_CXX_COMPILER_TARGET=%s", targetTriple))
}
if msvcTargetArchFlag == "arm" || msvcTargetArchFlag == "arm64" {
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DCMAKE_ASM_FLAGS=--target=%s", targetTriple))
}
if msvcCrtLinkageFlag == "dynamic" && (msvcTargetArchFlag == "x86" || msvcTargetArchFlag == "x64") {
cmakeArgs = append(cmakeArgs, "-DUSE_TCMALLOC=on")
}
// if msvcCrtLinkageFlag == "dynamic" {
// cmakeArgs = append(cmakeArgs, "-DUSE_MIMALLOC=on")
// }
}
if systemNameFlag == "darwin" {
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DCMAKE_OSX_DEPLOYMENT_TARGET=%s", macosxVersionMinFlag))
if macosxUniversalBuildFlag {
cmakeArgs = append(cmakeArgs, "-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64")
} else {
if archFlag == "x64" || archFlag == "x86_64" || archFlag == "amd64" {
cmakeArgs = append(cmakeArgs, "-DCMAKE_SYSTEM_PROCESSOR=x86_64")
cmakeArgs = append(cmakeArgs, "-DCMAKE_OSX_ARCHITECTURES=x86_64")
} else if archFlag == "arm64" {
cmakeArgs = append(cmakeArgs, "-DCMAKE_SYSTEM_PROCESSOR=arm64")
cmakeArgs = append(cmakeArgs, "-DCMAKE_OSX_ARCHITECTURES=arm64")
} else if archFlag == "" {
// nop
} else {
glog.Fatalf("Invalid archFlag: %s", archFlag)
}
}
}
if systemNameFlag == "mingw" {
if mingwDir != "" {
glog.Infof("Using llvm-mingw dir %s", mingwDir)
glog.Infof("Using clang dir %s", clangPath)
} else {
mingwDir = clangPath
glog.Infof("Using llvm-mingw dir %s", clangPath)
glog.Infof("Using clang inside llvm-mingw dir %s", mingwDir)
}
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DCROSS_TOOLCHAIN_FLAGS_TOOLCHAIN_FILE=%s/Native.cmake", buildDir))
ccCompiler := os.Getenv("CC")
cxxCompiler := os.Getenv("CXX")
nativeToolChainContent := strings.Replace(fmt.Sprintf("set(CMAKE_C_COMPILER \"%s\")\n", ccCompiler), "\\", "/", -1)
nativeToolChainContent += strings.Replace(fmt.Sprintf("set(CMAKE_CXX_COMPILER \"%s\")\n", cxxCompiler), "\\", "/", -1)
err := ioutil.WriteFile("Native.cmake", []byte(nativeToolChainContent), 0666)
if err != nil {
glog.Fatalf("%v", err)
}
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DUSE_CARES=%s", "ON"))
targetTriple, targetAbi := getMinGWTargetAndAppAbi(archFlag)
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DHOST_OS=%s", runtime.GOOS))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DCMAKE_TOOLCHAIN_FILE=%s/../cmake/platforms/MinGW.cmake", buildDir))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DLLVM_SYSROOT=%s", clangPath))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DMINGW_SYSROOT=%s", mingwDir))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DGCC_SYSTEM_PROCESSOR=%s", targetAbi))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DGCC_TARGET=%s", targetTriple))
if mingwAllowXpFlag {
cmakeArgs = append(cmakeArgs, "-DALLOW_XP=ON")
// we need msvcrt runtime for windows xp build
if targetAbi == "i686" {
cmakeArgs = append(cmakeArgs, "-DMINGW_MSVCRT100=ON")
}
llvm_version := getClangVersion(clangPath)
clang_rt_suffix := targetAbi
if targetAbi == "i686" {
clang_rt_suffix = "i386"
}
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DMINGW_COMPILER_RT=%s/lib/clang/%s/lib/windows/libclang_rt.builtins-%s.a", clangPath, llvm_version, clang_rt_suffix))
} else {
cmakeArgs = append(cmakeArgs, "-DALLOW_XP=OFF")
}
if mingwDir != clangPath {
getAndFixMinGWLibunwind(mingwDir)
}
if targetAbi == "i686" || targetAbi == "x86_64" {
cmakeArgs = append(cmakeArgs, "-DUSE_TCMALLOC=on")
}
// if !mingwAllowXpFlag && targetAbi != "i686" {
// cmakeArgs = append(cmakeArgs, "-DUSE_MIMALLOC=on")
// }
}
if systemNameFlag == "ios" {
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DCMAKE_TOOLCHAIN_FILE=%s/../cmake/platforms/ios.toolchain.cmake", buildDir))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DDEPLOYMENT_TARGET=%s", iosVersionMinFlag))
platform := "OS"
glog.Warning("No Packaging supported for ios, disabling...")
noPackagingFlag = true
if subSystemNameFlag == "simulator" {
if archFlag == "x86" {
platform = "SIMULATOR"
} else if archFlag == "x64" || archFlag == "x86_64" || archFlag == "amd64" {
platform = "SIMULATOR64"
} else if archFlag == "arm64" {
platform = "SIMULATORARM64"
} else {
glog.Fatalf("Invalid archFlag: %s", archFlag)
}
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DXCODE_CODESIGN_IDENTITY=%s", iosCodeSignIdentityFlag))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DXCODE_DEPLOYMENT_TEAM=%s", iosDevelopmentTeamFlag))
} else if subSystemNameFlag != "" {
glog.Fatalf("Invalid subSystemNameFlag: %s", subSystemNameFlag)
} else {
if archFlag == "arm" {
platform = "OS"
} else if archFlag == "arm64" {
platform = "OS64"
} else {
glog.Fatalf("Invalid archFlag: %s", archFlag)
}
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DXCODE_CODESIGN_IDENTITY=%s", iosCodeSignIdentityFlag))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DXCODE_DEPLOYMENT_TEAM=%s", iosDevelopmentTeamFlag))
cmakeArgs = append(cmakeArgs, "-DIOS_XCODE_DSYM_FOLDER_FIX=on")
}
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DPLATFORM=%s", platform))
cmakeArgs = append(cmakeArgs, "-DENABLE_STRICT_TRY_COMPILE=on")
}
if systemNameFlag == "android" {
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DUSE_CARES=%s", "ON"))
glog.Infof("Using android ndk version %s", androidNdkVer)
NdkDir := filepath.Join(androidSdkDir, "ndk", androidNdkVer)
if _, err := os.Stat(NdkDir); errors.Is(err, os.ErrNotExist) {
glog.Fatalf("Android Ndk Directory at %s demanded", NdkDir)
}
glog.Infof("Using android ndk dir %s", NdkDir)
androidAbiTarget, androidAppAbi := getAndroidTargetAndAppAbi(archFlag)
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DCMAKE_TOOLCHAIN_FILE=%s/../cmake/platforms/Android.cmake", buildDir))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DANDROID_ABI=%s", androidAppAbi))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DANDROID_ABI_TARGET=%s", androidAbiTarget))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DANDROID_API_VERSION=%d", androidApiLevel))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DANDROID_NDK_ROOT=%s", NdkDir))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DANDROID_CURRENT_OS=%s", runtime.GOOS))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DLLVM_SYSROOT=%s", clangPath))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DGCC_SYSTEM_PROCESSOR=%s", androidAppAbi))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DGCC_TARGET=%s%d", androidAbiTarget, androidApiLevel))
// FIXME patch llvm toolchain to find libunwind.a
getAndFixAndroidLibunwind(NdkDir)
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DENABLE_FORTIFY=on"))
}
if systemNameFlag == "harmony" {
if _, err := os.Stat(harmonyNdkDir); errors.Is(err, os.ErrNotExist) {
glog.Fatalf("Harmony Ndk Directory at %s demanded", harmonyNdkDir)
}
glog.Infof("Using harmony ndk dir %s", harmonyNdkDir)
_, harmonyAppAbi := getHarmonyTargetAndAppAbi(archFlag)
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DCMAKE_TOOLCHAIN_FILE=%s/../cmake/platforms/Harmony.cmake", buildDir))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DOHOS_ARCH=%s", harmonyAppAbi))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DOHOS_SDK_NATIVE=%s/native", harmonyNdkDir))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DLLVM_SYSROOT=%s", clangPath))
getAndFixHarmonyLibunwind()
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DENABLE_FORTIFY=on"))
}
if systemNameFlag == "linux" && sysrootFlag != "" {
subsystem := subSystemNameFlag
if subSystemNameFlag == "openwrt" {
subsystem = "musl"
}
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DUSE_CARES=%s", "ON"))
gnuType, gnuArch := getGNUTargetTypeAndArch(archFlag, subsystem)
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DCMAKE_TOOLCHAIN_FILE=%s/../cmake/platforms/Linux.cmake", buildDir))
var pkgConfigPath = filepath.Join(sysrootFlag, "usr", "lib", "pkgconfig")
pkgConfigPath += ";" + filepath.Join(sysrootFlag, "usr", "share", "pkgconfig")
if err := os.Setenv("PKG_CONFIG_PATH", pkgConfigPath); err != nil {
glog.Fatalf("%v", err)
}
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DLLVM_SYSROOT=%s", clangPath))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DGCC_SYSROOT=%s", sysrootFlag))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DGCC_SYSTEM_PROCESSOR=%s", gnuArch))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DGCC_TARGET=%s", gnuType))
if (archFlag == "arm64" || archFlag == "aarch64") && armCpuFlag != "" {
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DARM_CPU=%s", armCpuFlag))
}
if subsystem == "" {
cmakeArgs = append(cmakeArgs, "-DUSE_TCMALLOC=on")
} else if subsystem == "musl" {
cmakeArgs = append(cmakeArgs, "-DUSE_TCMALLOC=off")
// mimalloc calls madvise internally while
// some old system doesn't like it.
cmakeArgs = append(cmakeArgs, "-DUSE_MIMALLOC=off")
} else {
cmakeArgs = append(cmakeArgs, "-DUSE_TCMALLOC=off")
cmakeArgs = append(cmakeArgs, "-DUSE_MIMALLOC=off")
}
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DENABLE_FORTIFY=on"))
}
if systemNameFlag == "freebsd" && sysrootFlag != "" {
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DUSE_CARES=%s", "ON"))
var llvmTarget string
var llvmArch string
if archFlag == "amd64" || archFlag == "x86_64" {
llvmTarget = "x86_64-freebsd"
llvmArch = "x86_64"
} else if archFlag == "x86" || archFlag == "i386" {
llvmTarget = "i386-freebsd"
llvmArch = "i386"
} else if archFlag == "aarch64" || archFlag == "arm64" {
llvmTarget = "aarch64-freebsd"
llvmArch = "aarch64"
} else {
glog.Fatalf("Invalid arch: %s", archFlag)
}
llvmTarget = fmt.Sprintf("%s%d", llvmTarget, freebsdAbiFlag)
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DCMAKE_TOOLCHAIN_FILE=%s/../cmake/platforms/FreeBSD.cmake", buildDir))
var pkgConfigPath = filepath.Join(sysrootFlag, "usr", "libdata", "pkgconfig")
pkgConfigPath += ";" + filepath.Join(sysrootFlag, "usr", "local", "libdata", "pkgconfig")
if err := os.Setenv("PKG_CONFIG_PATH", pkgConfigPath); err != nil {
glog.Fatalf("%v", err)
}
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DFREEBSD_ABI_VERSION=%d", freebsdAbiFlag))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DLLVM_SYSROOT=%s", clangPath))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DGCC_SYSROOT=%s", sysrootFlag))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DGCC_SYSTEM_PROCESSOR=%s", llvmArch))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DGCC_TARGET=%s", llvmTarget))
cmakeArgs = append(cmakeArgs, fmt.Sprintf("-DENABLE_FORTIFY=on"))
// FIXME not enabled due to linkage issue
cmakeArgs = append(cmakeArgs, "-DUSE_TCMALLOC=off")
cmakeArgs = append(cmakeArgs, "-DUSE_MIMALLOC=off")
}
cmakeCmd := append([]string{"cmake", ".."}, cmakeArgs...)
if noConfigureFlag {
return
}
cmdRun(cmakeCmd, true)
}
func renameByUnlink(src string, dst string) error {
if _, err := os.Stat(dst); err == nil {
err = os.RemoveAll(dst)
if err != nil {
return err
}
}
return os.Rename(src, dst)
}
func buildStageExecuteBuildScript() {
glog.Info("BuildStage -- Execute Build Script")
glog.Info("======================================================================")
cmakeCmd := []string{"cmake", "--build", ".",
"--config", cmakeBuildTypeFlag,
"--parallel", fmt.Sprintf("%d", cmakeBuildConcurrencyFlag),
"--target", APPNAME}
if !(systemNameFlag == "ios" && (runBenchmarkFlag || runTestFlag)) {
cmdRun(cmakeCmd, true)
}
if buildBenchmarkFlag || runBenchmarkFlag {
cmakeCmd := []string{"cmake", "--build", ".",
"--config", cmakeBuildTypeFlag,
"--parallel", fmt.Sprintf("%d", cmakeBuildConcurrencyFlag),
"--target", "yass_benchmark"}
if !(systemNameFlag == "ios" && runBenchmarkFlag) {
cmdRun(cmakeCmd, true)
}
}
if runBenchmarkFlag {
if systemNameFlag == "ios" && subSystemNameFlag == "simulator" {
xcodeCmd := []string{"xcodebuild", "test", "-configuration", cmakeBuildTypeFlag,
"-jobs", fmt.Sprintf("%d", cmakeBuildConcurrencyFlag),
"-scheme", "yass", "-destination", "platform=iOS Simulator,name=iPhone 16"}
if !runTestFlag {
cmdRun(xcodeCmd, true)
}
} else if systemNameFlag == "ios" {
xcodeCmd := []string{"xcodebuild", "test", "-configuration", cmakeBuildTypeFlag,
"-jobs", fmt.Sprintf("%d", cmakeBuildConcurrencyFlag),
"-scheme", "yass", "-destination", "platform=iOS,name=" + iosTestDeviceNameFlag}
if !runTestFlag {
cmdRun(xcodeCmd, true)
}
} else {
checkCmd := []string{"./yass_benchmark"}
if verboseFlag > 0 {
checkCmd = []string{"./yass_benchmark", "-v", fmt.Sprintf("%d", verboseFlag), "-logtostderr"}
}
cmdRun(checkCmd, true)
}
}
if buildTestFlag || runTestFlag {
cmakeCmd := []string{"cmake", "--build", ".",
"--config", cmakeBuildTypeFlag,
"--parallel", fmt.Sprintf("%d", cmakeBuildConcurrencyFlag),
"--target", "yass_test"}
if !(systemNameFlag == "ios" && runTestFlag) {
cmdRun(cmakeCmd, true)
}
}
if runTestFlag {
if systemNameFlag == "ios" && subSystemNameFlag == "simulator" {
xcodeCmd := []string{"xcodebuild", "test", "-configuration", cmakeBuildTypeFlag,
"-jobs", fmt.Sprintf("%d", cmakeBuildConcurrencyFlag),
"-scheme", "yass", "-destination", "platform=iOS Simulator,name=iPhone 16"}
cmdRun(xcodeCmd, true)
} else if systemNameFlag == "ios" {
xcodeCmd := []string{"xcodebuild", "test", "-configuration", cmakeBuildTypeFlag,
"-jobs", fmt.Sprintf("%d", cmakeBuildConcurrencyFlag),
"-scheme", "yass", "-destination", "platform=iOS,name=" + iosTestDeviceNameFlag}
cmdRun(xcodeCmd, true)
} else {
checkCmd := []string{"./yass_test"}
if verboseFlag > 0 {
checkCmd = []string{"./yass_test", "-v", fmt.Sprintf("%d", verboseFlag), "-logtostderr"}
}
cmdRun(checkCmd, true)
}
}
// FIXME move to cmake (required by Xcode generator)
if systemNameFlag == "darwin" {
if _, err := os.Stat(filepath.Join(cmakeBuildTypeFlag, getAppName())); err == nil {
err := renameByUnlink(filepath.Join(cmakeBuildTypeFlag, getAppName()), getAppName())
glog.Fatalf("%v", err)
}
}
}
func GetWin32SearchPath() []string {
// no search when using libc++,
// TODO test under shaded libc++
// VCToolsVersion:PlatformToolchainversion:VisualStudioVersion
// 14.30-14.??:v143:Visual Studio 2022
// 14.20-14.29:v142:Visual Studio 2019
// 14.10-14.19:v141:Visual Studio 2017
// 14.00-14.00:v140:Visual Studio 2015
//
// From wiki: https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B
// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170#microsoft-specific-predefined-macros
// 6.00 Visual Studio 6.0 1200
// 7.00 Visual Studio .NET 2002 (7.0) 1300
// 7.10 Visual Studio .NET 2003 (7.1) 1310
// 8.00 Visual Studio 2005 (8.0) 1400
// 9.00 Visual Studio 2008 (9.0) 1500
// 10.00 Visual Studio 2010 (10.0) 1600
// 12.00 Visual Studio 2013 (12.0) 1800
// 14.00 Visual Studio 2015 (14.0) 1900
// 14.10 Visual Studio 2017 RTW (15.0) 1910
// 14.11 Visual Studio 2017 version 15.3 1911
// 14.12 Visual Studio 2017 version 15.5 1912
// 14.13 Visual Studio 2017 version 15.6 1913
// 14.14 Visual Studio 2017 version 15.7 1914
// 14.15 Visual Studio 2017 version 15.8 1915
// 14.16 Visual Studio 2017 version 15.9 1916
// 14.20 Visual Studio 2019 RTW (16.0) 1920
// 14.21 Visual Studio 2019 version 16.1 1921
// 14.22 Visual Studio 2019 version 16.2 1922
// 14.23 Visual Studio 2019 version 16.3 1923
// 14.24 Visual Studio 2019 version 16.4 1924
// 14.25 Visual Studio 2019 version 16.5 1925
// 14.26 Visual Studio 2019 version 16.6 1926
// 14.27 Visual Studio 2019 version 16.7 1927
// 14.28 Visual Studio 2019 version 16.8, 16.9 1928
// 14.29 Visual Studio 2019 version 16.10, 16.11 1929
// 14.30 Visual Studio 2022 RTW (17.0) 1930
// 14.31 Visual Studio 2022 version 17.1 1931
// 14.32 Visual Studio 2022 version 17.2 1932
// 14.33 Visual Studio 2022 version 17.3 1933
// 14.34 Visual Studio 2022 version 17.4 1934
// 14.35 Visual Studio 2022 version 17.5 1935
// 14.36 Visual Studio 2022 version 17.6 1936
// 14.37 Visual Studio 2022 version 17.7 1937
// 14.38 Visual Studio 2022 version 17.8 1938
// 14.39 Visual Studio 2022 version 17.9 1939
// 14.40 Visual Studio 2022 version 17.10 1940
// 14.41 Visual Studio 2022 version 17.11 1941
//
// Visual Studio 2015 is not supported by this script due to
// the missing environment variable VCToolsVersion
vctoolsVersionStr := os.Getenv("VCToolsVersion")
var vctoolsVersion float64
if len(vctoolsVersionStr) >= 4 {
// for vc141 or above, VCToolsVersion=14.??.xxxx
vctoolsVersion, _ = strconv.ParseFloat(vctoolsVersionStr[:5], 32)
} else {
// for vc140 or below, VCToolsVersion=14.0
vctoolsVersion, _ = strconv.ParseFloat(vctoolsVersionStr, 32)
}
var platformToolchainVersion string
if vctoolsVersion >= 14.30 {
platformToolchainVersion = "143"
} else if vctoolsVersion >= 14.20 && vctoolsVersion < 14.30 {
platformToolchainVersion = "142"
} else if vctoolsVersion >= 14.10 && vctoolsVersion < 14.20 {
platformToolchainVersion = "141"
} else if vctoolsVersion >= 14.00 && vctoolsVersion < 14.10 {
platformToolchainVersion = "140"
} else {
glog.Fatalf("unsupported vctoolchain version %f", vctoolsVersion)
}
// Environment variable VCToolsRedistDir isn't correct when it doesn't match
// Visual Studio's default toolchain.
// according to microsoft's design issue, don't use it.
//
// for example:
//
// VCINSTALLDIR=C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\
// VCToolsInstallDir=C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.16.27023\
// VCToolsRedistDir=C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Redist\MSVC\14.30.30704\
//
// for vc140 it's:
// C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist
var vcredistDir string
if vctoolsVersion >= 14.30 {
vcredistDir = os.Getenv("VCToolsRedistDir")
} else if vctoolsVersion >= 14.20 && vctoolsVersion < 14.30 {
vcredistDir = filepath.Join(os.Getenv("VCINSTALLDIR"), "Redist", "MSVC", os.Getenv("VCToolsVersion"))
// fallback
if _, err := os.Stat(vcredistDir); errors.Is(err, os.ErrNotExist) {
vcredistDir = filepath.Join(os.Getenv("VCINSTALLDIR"), "Redist", "MSVC", "14.29.30133")
}
} else if vctoolsVersion >= 14.10 && vctoolsVersion < 14.20 {
vcredistDir = filepath.Join(os.Getenv("VCINSTALLDIR"), "Redist", "MSVC", os.Getenv("VCToolsVersion"))
// fallback
if _, err := os.Stat(vcredistDir); errors.Is(err, os.ErrNotExist) {
vcredistDir = filepath.Join(os.Getenv("VCINSTALLDIR"), "Redist", "MSVC", "14.16.27012")
}
} else if vctoolsVersion >= 14.00 && vctoolsVersion < 14.10 {
vcredistDir = filepath.Join(os.Getenv("VCINSTALLDIR"), "redist")
} else {
vcredistDir = ""
}
// Search Path (VC Runtime and MFC) (vctoolsVersion newer than v140)
// C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Redist\MSVC\14.30.30704\debug_nonredist\x86\Microsoft.VC143.DebugMFC
// C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Redist\MSVC\14.30.30704\debug_nonredist\x86\Microsoft.VC143.DebugCRT
// C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Redist\MSVC\14.30.30704\x86\Microsoft.VC143.MFCLOC
// C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Redist\MSVC\14.30.30704\x86\Microsoft.VC143.MFC
// C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Redist\MSVC\14.30.30704\x86\Microsoft.VC143.CRT
searchDirs := []string{
filepath.Join(vcredistDir, "debug_nonredist", msvcTargetArchFlag, fmt.Sprintf("Microsoft.VC%s.DebugMFC", platformToolchainVersion)),
filepath.Join(vcredistDir, "debug_nonredist", msvcTargetArchFlag, fmt.Sprintf("Microsoft.VC%s.DebugCRT", platformToolchainVersion)),
filepath.Join(vcredistDir, msvcTargetArchFlag, fmt.Sprintf("Microsoft.VC%s.MFCLOC", platformToolchainVersion)),
filepath.Join(vcredistDir, msvcTargetArchFlag, fmt.Sprintf("Microsoft.VC%s.MFC", platformToolchainVersion)),
filepath.Join(vcredistDir, msvcTargetArchFlag, fmt.Sprintf("Microsoft.VC%s.CRT", platformToolchainVersion)),
}
// remove the trailing slash
sdkVersionInt := os.Getenv("WindowsSDKVersion")
sdkVersion := sdkVersionInt[:len(sdkVersionInt)-1]
sdkBaseDir := os.Getenv("WindowsSdkDir")
// Search Path (UCRT)
//
// Please note The UCRT files are not redistributable for ARM64 Win32.
// https://chromium.googlesource.com/chromium/src/+/lkgr/build/win/BUILD.gn
// C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x86\ucrt
// C:\Program Files (x86)\Windows Kits\10\Redist\10.0.19041.0\ucrt\DLLS\x86
// C:\Program Files (x86)\Windows Kits\10\ExtensionSDKs\Microsoft.UniversalCRT.Debug\10.0.19041.0\Redist\Debug\x86
searchDirs = append(searchDirs, filepath.Join(sdkBaseDir, "bin", sdkVersion, msvcTargetArchFlag, "ucrt"))
searchDirs = append(searchDirs, filepath.Join(sdkBaseDir, "Redist", sdkVersion, "ucrt", "DLLS", msvcTargetArchFlag))
searchDirs = append(searchDirs, filepath.Join(sdkBaseDir, "ExtensionSDKs", "Microsoft.UniversalCRT.Debug", sdkVersion, "Redist", "Debug", msvcTargetArchFlag))
// Fallback search path for XP (v140_xp, v141_xp)
// Refer to #27, https://github.com/Chilledheart/yass/issues/27
// $project_root\third_party\vcredist\x86
if vctoolsVersion >= 14.00 && vctoolsVersion < 14.20 {
path, _ := filepath.Abs(filepath.Join("..", "third_party", "vcredist", msvcTargetArchFlag))
searchDirs = append(searchDirs, path)
}
return searchDirs
}
// The output of dumpbin /dependents is like below:
// Microsoft (R) COFF/PE Dumper Version 14.30.30709.0
// Copyright (C) Microsoft Corporation. All rights reserved.
// Dump of file arm64-windows-yass.exe
// File Type: EXECUTABLE IMAGE
//
// Image has the following dependencies:
// WS2_32.dll
// GDI32.dll
// SHELL32.dll
// KERNEL32.dll
// USER32.dll
// ADVAPI32.dll
// MSVCP140.dll
// MSWSOCK.dll
// mfc140u.dll
// dbghelp.dll
// VCRUNTIME140.dll
// api-ms-win-crt-runtime-l1-1-0.dll
// api-ms-win-crt-heap-l1-1-0.dll
// api-ms-win-crt-stdio-l1-1-0.dll
// api-ms-win-crt-math-l1-1-0.dll
// api-ms-win-crt-time-l1-1-0.dll
// api-ms-win-crt-filesystem-l1-1-0.dll
// api-ms-win-crt-convert-l1-1-0.dll
// api-ms-win-crt-environment-l1-1-0.dll
// api-ms-win-crt-string-l1-1-0.dll
// api-ms-win-crt-locale-l1-1-0.dll
// Summary
// 15000 .data
// 4000 .pdata
// 2C000 .rdata
// 2000 .reloc
// 12000 .rsrc
// 71000 .text
func GetDependenciesByDumpbin(path string, searchDirs []string) ([]string, []string) {
lines := strings.Split(cmdCheckOutput([]string{"dumpbin", "/nologo", "/dependents", path}), "\n")
dlls := []string{}
resolvedDlls := []string{}
unresolvedDlls := []string{}
systemDlls := []string{"WS2_32.dll", "GDI32.dll", "SHELL32.dll", "USER32.dll",
"ADVAPI32.dll", "MSWSOCK.dll", "dbghelp.dll", "KERNEL32.dll",
"ole32.dll", "OLEAUT32.dll", "SHLWAPI.dll", "IMM32.dll",
"UxTheme.dll", "PROPSYS.dll", "dwmapi.dll", "WININET.dll",
"OLEACC.dll", "ODBC32.dll", "oledlg.dll", "urlmon.dll",
"MSIMG32.dll", "WINMM.dll", "CRYPT32.dll", "gdiplus.dll",
"COMCTL32.dll", "RASAPI32.dll", "IPHLPAPI.DLL",
"WINHTTP.dll", "VERSION.dll", "POWRPROF.dll",
}
systemDllsMap := make(map[string]struct{})
for _, dll := range systemDlls {
systemDllsMap[strings.ToLower(dll)] = struct{}{}
}
p := regexp.MustCompile("(?i) (\\S+.dll)")
for _, line := range lines {
matches := p.FindStringSubmatch(line)
if len(matches) > 1 {
_, ok := systemDllsMap[strings.ToLower(matches[1])]
if !ok {
dlls = append(dlls, matches[1])
}
}
}
// append crt dlls
if msvcCrtLinkageFlag == "dynamic" {
vctoolsVersionStr := os.Getenv("VCToolsVersion")
var vctoolsVersion float64
if len(vctoolsVersionStr) >= 4 {
// for vc141 or above, VCToolsVersion=14.??.xxxx
vctoolsVersion, _ = strconv.ParseFloat(vctoolsVersionStr[:5], 32)
} else {
// for vc140 or below, VCToolsVersion=14.0
vctoolsVersion, _ = strconv.ParseFloat(vctoolsVersionStr, 32)
}
if vctoolsVersion >= 14.30 {
if msvcTargetArchFlag == "x64" || msvcTargetArchFlag == "arm64" {
dlls = append(dlls, "concrt140.dll", "msvcp140.dll", "msvcp140_1.dll", "msvcp140_2.dll",
"msvcp140_atomic_wait.dll", "msvcp140_codecvt_ids.dll", "vccorlib140.dll", "vcruntime140.dll", "vcruntime140_1.dll")
} else if msvcTargetArchFlag == "x86" {
dlls = append(dlls, "concrt140.dll", "msvcp140.dll", "msvcp140_1.dll", "msvcp140_2.dll",
"msvcp140_atomic_wait.dll", "msvcp140_codecvt_ids.dll", "vccorlib140.dll", "vcruntime140.dll")
}
} else if vctoolsVersion >= 14.20 && vctoolsVersion < 14.30 {
if msvcTargetArchFlag == "x64" || msvcTargetArchFlag == "arm64" {
dlls = append(dlls, "concrt140.dll", "msvcp140.dll", "msvcp140_1.dll", "msvcp140_2.dll",
"msvcp140_atomic_wait.dll", "msvcp140_codecvt_ids.dll", "vccorlib140.dll", "vcruntime140.dll", "vcruntime140_1.dll")
} else if msvcTargetArchFlag == "x86" {
dlls = append(dlls, "concrt140.dll", "msvcp140.dll", "msvcp140_1.dll", "msvcp140_2.dll",
"msvcp140_atomic_wait.dll", "msvcp140_codecvt_ids.dll", "vccorlib140.dll", "vcruntime140.dll")
}
} else if vctoolsVersion >= 14.10 && vctoolsVersion < 14.20 {
if msvcTargetArchFlag == "x64" {
dlls = append(dlls, "concrt140.dll", "msvcp140.dll", "msvcp140_1.dll", "msvcp140_2.dll",
"vccorlib140.dll", "vcruntime140.dll")
} else if msvcTargetArchFlag == "x86" {
dlls = append(dlls, "concrt140.dll", "msvcp140.dll", "msvcp140_1.dll", "msvcp140_2.dll",
"vccorlib140.dll", "vcruntime140.dll")
}
} else if vctoolsVersion >= 14.00 && vctoolsVersion < 14.10 {
if msvcTargetArchFlag == "x64" {
dlls = append(dlls, "concrt140.dll", "msvcp140.dll", "msvcp140_1.dll", "msvcp140_2.dll",
"vccorlib140.dll", "vcruntime140.dll")
} else if msvcTargetArchFlag == "x86" {
dlls = append(dlls, "concrt140.dll", "msvcp140.dll", "msvcp140_1.dll", "msvcp140_2.dll",
"vccorlib140.dll", "vcruntime140.dll")
}
} else {
// nop
}
if cmakeBuildTypeFlag == "Debug" {
dlls = append(dlls, "ucrtbased.dll")
} else {
dlls = append(dlls, "ucrtbase.dll")
dlls = append(dlls, "api-ms-win-crt-conio-l1-1-0.dll",
"api-ms-win-crt-convert-l1-1-0.dll",
"api-ms-win-crt-environment-l1-1-0.dll",
"api-ms-win-crt-filesystem-l1-1-0.dll",
"api-ms-win-crt-heap-l1-1-0.dll",
"api-ms-win-crt-locale-l1-1-0.dll",
"api-ms-win-crt-math-l1-1-0.dll",
"api-ms-win-crt-multibyte-l1-1-0.dll",
"api-ms-win-crt-private-l1-1-0.dll",
"api-ms-win-crt-process-l1-1-0.dll",
"api-ms-win-crt-runtime-l1-1-0.dll",
"api-ms-win-crt-stdio-l1-1-0.dll",
"api-ms-win-crt-string-l1-1-0.dll",
"api-ms-win-crt-time-l1-1-0.dll",
"api-ms-win-crt-utility-l1-1-0.dll")
}
}
for _, dll := range dlls {
resolved := false
for _, searchDir := range searchDirs {
d := filepath.Join(searchDir, dll)
if _, err := os.Stat(d); !errors.Is(err, os.ErrNotExist) {
resolvedDlls = append(resolvedDlls, d)
resolved = true
break
}
}
if !resolved {
unresolvedDlls = append(unresolvedDlls, dll)
}
}
return resolvedDlls, unresolvedDlls
}
func postStateCopyDependedLibraries() {
glog.Info("PostState -- Copy Depended Libraries")
glog.Info("======================================================================")
searchDirs := []string{}
if systemNameFlag == "windows" {
searchDirs = append(searchDirs, GetWin32SearchPath()...)
for _, searchDir := range searchDirs {
glog.Infof("--- %s", searchDir)
}
deps, unresolvedDeps := GetDependenciesByDumpbin(getAppName(), searchDirs)
hasCrashpad := true
if _, err := os.Stat("crashpad_handler.exe"); errors.Is(err, os.ErrNotExist) {
hasCrashpad = false
}
if hasCrashpad {
deps = append(deps, "crashpad_handler.exe")
}
depsMap := make(map[string]struct{})
for _, dep := range deps {
depsMap[dep] = struct{}{}
}
for true {
depsExtended := deps
for _, dep := range deps {
depDeps, depUnresolvedDeps := GetDependenciesByDumpbin(dep, searchDirs)
depsExtended = append(depsExtended, depDeps...)
unresolvedDeps = append(unresolvedDeps, depUnresolvedDeps...)
}
depsExtendedMap := make(map[string]struct{})
for _, dep := range depsExtended {
depsExtendedMap[dep] = struct{}{}
}
if reflect.DeepEqual(depsMap, depsExtendedMap) {
break
}
depsExtended = []string{}
for dep := range depsExtendedMap {
depsExtended = append(depsExtended, dep)
}
deps = depsExtended
depsMap = depsExtendedMap
}
unresolvedDepsMap := make(map[string]struct{})
for _, unresolvedDep := range unresolvedDeps {
unresolvedDepsMap[unresolvedDep] = struct{}{}
}
glog.Infof("--- Dependent dll")
for _, dep := range deps {
glog.Infof("--- --- %s", dep)
if !strings.HasSuffix(strings.ToLower(dep), ".dll") {
continue
}
err := copyFile(dep, filepath.Base(dep))
if err != nil {
glog.Fatalf("Copy file %s failed :%v", dep, err)
}
}
glog.Infof("--- Missing dependent dll")
for unresolvedDep, _ := range unresolvedDepsMap {
glog.Infof("--- --- %s", unresolvedDep)
}
// TBD
}
}
func postStateFixRPath() {
glog.Info("PostState -- Fix RPATH")
glog.Info("======================================================================")
// TBD
}
func postStateStripBinaries() {
glog.Info("PostState -- Strip Binaries")
glog.Info("======================================================================")
if systemNameFlag == "windows" {
return
}
if systemNameFlag == "ios" {
glog.Info("Done in xcodebuild")
return
}
if systemNameFlag == "mingw" || systemNameFlag == "harmony" || systemNameFlag == "linux" || systemNameFlag == "freebsd" {
objcopy := filepath.Join(clangPath, "bin", "llvm-objcopy")
if runtime.GOOS == "windows" {
objcopy = filepath.Join(clangPath, "bin", "llvm-objcopy.exe")
}
if _, err := os.Stat(objcopy); errors.Is(err, os.ErrNotExist) {
objcopy = "objcopy"
}
// create a file containing the debugging info.
cmdRun([]string{objcopy, "--only-keep-debug", getAppName(), getAppName() + ".dbg"}, false)
// stripped executable.
cmdRun([]string{objcopy, "--strip-debug", getAppName()}, false)
// to add a link to the debugging info into the stripped executable.
cmdRun([]string{objcopy, "--add-gnu-debuglink=" + getAppName() + ".dbg", getAppName()}, false)
} else if systemNameFlag == "darwin" {
cmdRun([]string{"dsymutil", filepath.Join(getAppName(), "Contents", "MacOS", APPNAME),
"--statistics", "--update", "-o", getAppName() + ".dSYM"}, false)
cmdRun([]string{"strip", "-S", "-x", "-v", filepath.Join(getAppName(), "Contents", "MacOS", APPNAME)}, false)
} else if systemNameFlag == "ios" {
cmdRun([]string{"dsymutil", filepath.Join(getAppName(), APPNAME),
"--statistics", "--update", "-o", getAppName() + ".dSYM"}, false)
cmdRun([]string{"strip", "-S", "-x", "-v", filepath.Join(getAppName(), APPNAME)}, false)
} else {
glog.Warningf("not supported in platform %s", systemNameFlag)
}
}
func postStateCodeSign() {
glog.Info("PostState -- Code Sign")
glog.Info("======================================================================")
if systemNameFlag == "ios" {
glog.Info("Done in xcodebuild")
return
}
if cmakeBuildTypeFlag != "Release" || (systemNameFlag != "darwin" && systemNameFlag != "ios") {
return
}
// reference https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/resolving_common_notarization_issues?language=objc
// Hardened runtime is available in the Capabilities pane of Xcode 10 or later
// code sign crashpad_handler as well if any
hasCrashpad := true
crashpadPath := filepath.Join(getAppName(), "Contents", "Resources", "crashpad_handler")
if _, err := os.Stat(crashpadPath); errors.Is(err, os.ErrNotExist) {
hasCrashpad = false
}
codesignCmd := []string{
"codesign", "-s", macosxCodeSignIdentityFlag,
"--deep", "--force", "--options=runtime", "--timestamp",
"--entitlements=" + filepath.Join(projectDir, "src", "mac", "entitlements.plist"),
}
if macosxKeychainPathFlag != "" {
codesignCmd = append(codesignCmd, "--keychain", macosxKeychainPathFlag)
}
if hasCrashpad {
codesignCmd := append(codesignCmd, crashpadPath)
cmdRun(codesignCmd, true)
}
codesignCmd = append(codesignCmd, getAppName())
cmdRun(codesignCmd, true)
cmdRun([]string{"codesign", "-dv", "--deep", "--strict", "--verbose=4", getAppName()}, true)
cmdRun([]string{"codesign", "-d", "--entitlements", ":-", getAppName()}, true)
if hasCrashpad {
cmdRun([]string{"codesign", "-d", "--entitlements", ":-", crashpadPath}, true)
}
cmdRun([]string{"spctl", "-a", "-vvv", "--type", "install", getAppName()}, false)
}
// Main returns the file name excluding extension.
func Main(path string) string {
path = filepath.Base(path)
for i := len(path) - 1; i >= 0 && !os.IsPathSeparator(path[i]); i-- {
if path[i] == '.' {
return path[:i]
}
}
return path
}
func checkUniversalBuildFatBundle(bundlePath string) bool {
mainFile := filepath.Join(bundlePath, "Contents", "MacOS", Main(bundlePath))
// TODO should we check recursilvely?
return checkUniversalBuildFatBinary(mainFile)
}
func checkUniversalBuildFatBinary(binaryFile string) bool {
output := cmdCheckOutput([]string{"lipo", "-archs", binaryFile})
return strings.Contains(output, "x86_64") && strings.Contains(output, "arm64")
}
func checkUniveralBuild(path string) error {
checked := true
statusMsg := ""
errorMsg := ""
if strings.HasSuffix(path, ".framework") || strings.HasSuffix(path, ".app") || strings.HasSuffix(path, ".appex") {
if !checkUniversalBuildFatBundle(path) {
statusMsg += fmt.Sprintf("bundle %s is not universal built\n", path)
errorMsg += fmt.Sprintf("bundle %s is not universal built\n", path)
checked = false
} else {
statusMsg += fmt.Sprintf("bundle %s is universal built\n", path)
}
walkFunc := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
if strings.HasSuffix(path, ".framework") || strings.HasSuffix(path, ".app") || strings.HasSuffix(path, ".appex") {
if !checkUniversalBuildFatBundle(path) {
statusMsg += fmt.Sprintf("bundle %s is not universal built\n", path)
errorMsg += fmt.Sprintf("bundle %s is not universal built\n", path)
checked = false
} else {
statusMsg += fmt.Sprintf("bundle %s is universal built\n", path)
}
}
} else {
if strings.HasSuffix(path, ".so") || strings.HasSuffix(path, ".dylib") {
if !checkUniversalBuildFatBinary(path) {
statusMsg += fmt.Sprintf("dylib %s is not universal built\n", path)
errorMsg += fmt.Sprintf("dylib %s is not universal built\n", path)
checked = false
} else {
statusMsg += fmt.Sprintf("dylib %s is universal built\n", path)
}
}
}
return nil
}
err := filepath.Walk(path, walkFunc)
if err != nil {
return err
}
glog.V(2).Info(statusMsg)
}
if checked {
return nil
}
return errors.New(errorMsg)
}
func postCheckUniversalBuild() {
glog.Info("PostState -- Check Universal Build")
glog.Info("======================================================================")
if systemNameFlag == "darwin" && macosxUniversalBuildFlag {
err := checkUniveralBuild(getAppName())
if err != nil {
glog.Fatalf("%v", err)
}
}
}
func copyFile(src string, dst string) error {
buf := make([]byte, 32768)
fin, err := os.Open(src)
if err != nil {
return err
}
defer func(fin *os.File) {
err := fin.Close()
if err != nil {
glog.Fatalf("%v", err)
}
}(fin)
fout, err := os.Create(dst)
if err != nil {
return err
}
defer func(fout *os.File) {
err := fout.Close()
if err != nil {
glog.Fatalf("%v", err)
}
}(fout)
for {
n, err := fin.Read(buf)
if err != nil && err != io.EOF {
return err
}
if n == 0 {
break
}
if _, err := fout.Write(buf[:n]); err != nil {
return err
}
}
return nil
}
// LICENSEs use unified LICENSE
func postStateArchiveLicenses() []string {
var licenses []string
licenses = append(licenses, "LICENSE")
return licenses
}
// add to zip writer
func archiveFileToZip(zipWriter *zip.Writer, info os.FileInfo, prefix string, path string) error {
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
symlinksTarget, err := os.Readlink(path)
if err != nil {
return err
}
info, err := os.Lstat(path)
if err != nil {
return err
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
symlinksTarget = filepath.Join(prefix, symlinksTarget)
header.Name = filepath.Join(prefix, header.Name)
header.Method = zip.Deflate
writer, err := zipWriter.CreateHeader(header)
if err != nil {
return err
}
// Write symlink's target to writer - file's body for symlinks is the symlink target.
_, err = writer.Write([]byte(filepath.ToSlash(symlinksTarget)))
if err != nil {
return err
}
return nil
}
f, err := os.Open(path)
if err != nil {
return err
}
defer func(f *os.File) {
err := f.Close()
if err != nil {
glog.Fatalf("%v", err)
}
}(f)
w, err := zipWriter.Create(filepath.Join(prefix, path))
if err != nil {
return err
}
if _, err := io.Copy(w, f); err != nil {
return err
}
return nil
}
// generate tgz (posix) or zip file
func archiveFiles(output string, prefix string, paths []string) {
if strings.HasSuffix(output, ".tgz") {
glog.Infof("generating tgz file %s", output)
// use below if tar supports gnu style
// cmd := []string{"tar", "caf", output, "--xform", fmt.Sprintf("s,^,%s/,", prefix)}
cmd := []string{"mkdir", "-p", prefix}
cmdRun(cmd, true)
cmd = []string{"cp", "-rf"}
cmd = append(cmd, paths...)
cmd = append(cmd, prefix)
cmdRun(cmd, true)
cmd = []string{"tar", "cfz", output, prefix}
cmdRun(cmd, true)
cmd = []string{"rm", "-rf", prefix}
cmdRun(cmd, true)
return
}
glog.Infof("generating zip file %s", output)
archive, err := os.Create(output)
if err != nil {
glog.Fatalf("%v", err)
}
defer func(archive *os.File) {
err := archive.Close()
if err != nil {
glog.Fatalf("%v", err)
}
}(archive)
zipWriter := zip.NewWriter(archive)
for _, path := range paths {
info, err := os.Stat(path)
if err != nil {
glog.Fatalf("%v", err)
}
if !info.IsDir() {
err := archiveFileToZip(zipWriter, info, prefix, path)
if err != nil {
glog.Fatalf("%v", err)
}
continue
}
walkFunc := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
err := archiveFileToZip(zipWriter, info, prefix, path)
if err != nil {
return err
}
}
return nil
}
err = filepath.Walk(path, walkFunc)
if err != nil {
glog.Fatalf("%v", err)
}
}
err = zipWriter.Close()
if err != nil {
glog.Fatalf("%v", err)
}
}
func signOrCopyHapFile(inFile string, outFile string) error {
hapSignTool := fmt.Sprintf("%s/toolchains/lib/hap-sign-tool.jar", harmonyNdkDir)
keyAlias := getEnv("HARMONY_SIGNING_KEY_ALIAS", "")
keyPwd := getEnv("HARMONY_SIGNING_KEY_PASSWORD", "")
appCertFile := getEnv("HARMONY_SIGNING_CERTFILE", "")
profileFile := getEnv("HARMONY_SIGNING_PROFILE", "")
keystoreFile := getEnv("HARMONY_SIGNING_STORE_PATH", "")
keystorePwd := getEnv("HARMONY_SIGNING_STORE_PASSWORD", "")
if len(appCertFile) != 0 && len(profileFile) != 0 {
cmdRun([]string{
"java", "-jar", hapSignTool, "sign-app", "-keyAlias", keyAlias, "-signAlg", "SHA256withECDSA", "-mode", "localSign", "-appCertFile", appCertFile, "-profileFile", profileFile, "-inFile", inFile, "-keystoreFile", keystoreFile, "-outFile", outFile, "-keyPwd", keyPwd, "-keystorePwd", keystorePwd, "-signCode", "1"}, true)
} else {
return os.Rename(inFile, outFile)
}
return nil
}
func archiveMainFile(output string, prefix string, paths []string, dllPaths []string) {
if systemNameFlag == "darwin" {
var eulaRtf []byte
eulaTemplate, err := ioutil.ReadFile("../src/mac/eula.xml")
if err != nil {
glog.Fatalf("%v", err)
}
eulaRtf, err = ioutil.ReadFile("../GPL-2.0.rtf")
if err != nil {
glog.Fatalf("%v", err)
}
eulaXml := strings.Replace(string(eulaTemplate), "%PLACEHOLDER%", base64.StdEncoding.EncodeToString([]byte(eulaRtf)), -1)
err = ioutil.WriteFile("eula.xml", []byte(eulaXml), 0666)
if err != nil {
glog.Fatalf("%v", err)
}
// Use this command line to update .DS_Store
// hdiutil convert -format UDRW -o yass.dmg yass-macos-release-universal-*.dmg
// hdiutil resize -size 1G yass.dmg
cmdRun([]string{"../scripts/pkg-dmg",
"--source", paths[0],
"--target", output,
"--sourcefile",
"--volname", "Yet Another Shadow Socket",
"--resource", "eula.xml",
"--icon", "../src/mac/yass.icns",
"--copy", "../macos/.DS_Store:/.DS_Store",
"--copy", "../macos/.background:/",
"--symlink", "/Applications:/Applications"}, true)
} else if systemNameFlag == "ios" {
cmdRun([]string{"xcodebuild", "archive",
"-archivePath", cmakeBuildTypeFlag + ".xcarchive",
"-configuration", cmakeBuildTypeFlag,
"-jobs", fmt.Sprintf("%d", cmakeBuildConcurrencyFlag),
"-target", APPNAME, "-scheme", APPNAME}, true)
//
// copy dSYMs to xcarchive
//
xcarchiveSubdir := cmakeBuildTypeFlag + ".xcarchive"
cmdRun([]string{"rm", "-rf", filepath.Join(xcarchiveSubdir, "dSYMs", getAppName()+".dSYM")}, true)
cmdRun([]string{"rm", "-rf", filepath.Join(xcarchiveSubdir, "dSYMs", "YassPacketTunnel.appex.dSYM")}, true)
buildSubdir := cmakeBuildTypeFlag + "-iphoneos"
if subSystemNameFlag == "simulator" {
buildSubdir = cmakeBuildTypeFlag + "-iphonesimulator"
}
cmdRun([]string{"ditto", "-v", filepath.Join(buildSubdir, getAppName()+".dSYM"), filepath.Join(xcarchiveSubdir, "dSYMs", getAppName()+".dSYM")}, true)
cmdRun([]string{"ditto", "-v", filepath.Join(buildSubdir, "YassPacketTunnel.appex.dSYM"), filepath.Join(xcarchiveSubdir, "dSYMs", "YassPacketTunnel.appex.dSYM")}, true)
cmdRun([]string{"xcodebuild", "-exportArchive",
"-archivePath", cmakeBuildTypeFlag + ".xcarchive",
"-exportPath", ".",
"-exportOptionsPlist", "../tools/development.plist"}, true)
err := os.Rename("Yet Another Shadow Socket.ipa", output)
if err != nil {
glog.Fatalf("%v", err)
}
//
// cleanup after exportArchive
//
err = os.Chdir(buildSubdir)
if err != nil {
glog.Fatalf("%v", err)
}
cmdRun([]string{"rm", "-f", "YassPacketTunnel.appex", "libasio.a",
"libyass_crashpad.a", "libyass_core.a", "libyass_net.a", "yass.app"}, true)
err = os.Chdir(buildDir)
if err != nil {
glog.Fatalf("%v", err)
}
} else if systemNameFlag == "android" && variantFlag == "gui" {
androidDir := "../android"
err := os.Chdir(androidDir)
if err != nil {
glog.Fatalf("%v", err)
}
if androidSdkDir != "" {
glog.Infof("android sdk dir to %s", androidSdkDir)
localProperties := fmt.Sprintf("sdk.dir=%s\n", androidSdkDir)
localProperties += fmt.Sprintf("android.ndkVersion=%s\n", androidNdkVer)
tagVersionStrs := strings.Split(tagFlag, ".")
tagMajorVer, _ := strconv.Atoi(tagVersionStrs[0])
tagMinorVer, _ := strconv.Atoi(tagVersionStrs[1])
tagPatchVer, _ := strconv.Atoi(tagVersionStrs[2])
localProperties += fmt.Sprintf("YASS_VERSION=%d\n", tagMajorVer*1000000+tagMinorVer*1000+tagPatchVer)
localProperties += fmt.Sprintf("YASS_VERSION_NAME=%s\n", tagFlag)
err = ioutil.WriteFile("local.properties", []byte(localProperties), 0666)
if err != nil {
glog.Fatalf("%v", err)
}
}
_, abi := getAndroidTargetAndAppAbi(archFlag)
if cmakeBuildTypeFlag == "Release" || cmakeBuildTypeFlag == "MinSizeRel" {
if androidAab {
cmdRun([]string{"./gradlew", "yass:bundleRelease", "--parallel", "--info"}, true)
err = os.Rename("./yass/build/outputs/bundle/release/yass-release.aab", output)
} else {
cmdRun([]string{"./gradlew", "yass:assembleRelease", "--parallel", "--info"}, true)
err = os.Rename(fmt.Sprintf("./yass/build/outputs/apk/release/yass-%s-release.apk", abi), output)
}
if err != nil {
glog.Fatalf("%v", err)
}
} else {
if androidAab {
cmdRun([]string{"./gradlew", "yass:bundleDebug", "--parallel", "--info"}, true)
err = os.Rename("./yass/build/outputs/bundle/debug/yass-debug.aab", output)
} else {
cmdRun([]string{"./gradlew", "yass:assembleDebug", "--parallel", "--info"}, true)
err = os.Rename(fmt.Sprintf("./yass/build/outputs/apk/debug/yass-%s-release-unsigned.apk", abi), output)
}
if err != nil {
glog.Fatalf("%v", err)
}
}
// stop gradle daemon after build
cmdRun([]string{"./gradlew", "--stop"}, true)
err = os.Chdir(buildDir)
if err != nil {
glog.Fatalf("%v", err)
}
} else if systemNameFlag == "harmony" && variantFlag == "gui" {
harmonyDir := "../harmony"
err := os.Chdir(harmonyDir)
if err != nil {
glog.Fatalf("%v", err)
}
var build_type string
if cmakeBuildTypeFlag == "Release" || cmakeBuildTypeFlag == "MinSizeRel" {
build_type = "release"
} else {
build_type = "debug"
}
cmdRun([]string{"hvigorw", "clean", "--no-daemon"}, true)
cmdRun([]string{"rm", "-rf", "entry/libs"}, true)
_, abi := getHarmonyTargetAndAppAbi(archFlag)
cmdRun([]string{"mkdir", "-p", fmt.Sprintf("entry/libs/%s", abi)}, true)
cmdRun([]string{"cp", "-fv", fmt.Sprintf("../build-harmony-%s/libyass.so", archFlag), fmt.Sprintf("entry/libs/%s/", abi)}, true)
cmdRun([]string{"hvigorw", "assembleHap", "--mode", "module",
"-p", "product=default", "-p", fmt.Sprintf("buildMode=%s", build_type), "--no-daemon"}, true)
// optional signning step
err = signOrCopyHapFile("./entry/build/default/outputs/default/entry-default-unsigned.hap", output)
if err != nil {
glog.Fatalf("%v", err)
}
err = os.Chdir(buildDir)
if err != nil {
glog.Fatalf("%v", err)
}
} else {
paths = append(paths, dllPaths...)
archiveFiles(output, prefix, paths)
}
}
func generateMsi(output string, dllPaths []string, licensePaths []string, hasCrashpad bool) {
wxsTemplate, err := ioutil.ReadFile(filepath.Join("..", "yass.wxs"))
if err != nil {
glog.Fatalf("%v", err)
}
wxsXml := string(wxsTemplate)
guidReplacement := strings.ToUpper(uuid.New().String())
wxsXml = strings.Replace(wxsXml, "YOURGUID", guidReplacement, 1)
dllReplacement := ""
for _, dllPath := range dllPaths {
dllReplacement += fmt.Sprintf("<File Name='%s' Source='%s' KeyPath='no' />\n", filepath.Base(dllPath), dllPath)
}
wxsXml = strings.Replace(wxsXml, "<!-- %DLLPLACEHOLDER% -->", dllReplacement, 1)
licenseReplacement := ""
for _, licensePath := range licensePaths {
licenseReplacement += fmt.Sprintf("<File Name='%s' Source='%s' KeyPath='no' />\n", filepath.Base(licensePath), licensePath)
}
wxsXml = strings.Replace(wxsXml, "<!-- %LICENSEPLACEHOLDER% -->", licenseReplacement, 1)
if hasCrashpad {
wxsXml = strings.Replace(wxsXml, "<!-- %CRASHPAD_HANDLER_HOLDER% -->", "<File Name='crashpad_handler.exe' Source='crashpad_handler.exe' KeyPath='no'/>", 1)
}
err = ioutil.WriteFile("yass.wxs", []byte(wxsXml), 0666)
if err != nil {
glog.Fatalf("%v", err)
}
glog.Info("Feeding WiX compiler...")
cmdRun([]string{"candle.exe", "yass.wxs", "-dPlatform=" + msvcTargetArchFlag, "-dVersion=" + tagFlag}, true)
glog.Info("Generating MSI file...")
cmdRun([]string{"light.exe", "-ext", "WixUIExtension", "-out", output, "-cultures:en-US", "-sice:ICE03", "-sice:ICE57", "-sice:ICE61", "yass.wixobj"}, true)
}
func generateNSIS(output string, dllPaths []string) {
nsiTemplate, err := ioutil.ReadFile(filepath.Join("..", "yass.nsi"))
if err != nil {
glog.Fatalf("%v", err)
}
nsiContent := string(nsiTemplate)
nsiContent = strings.Replace(nsiContent, "yass-installer.exe", output, 1)
dllReplacement := ""
for _, dllPath := range dllPaths {
dllReplacement += fmt.Sprintf("File \"%s\"\n", dllPath)
}
nsiContent = strings.Replace(nsiContent, "# DLL PLACEHOLDER", dllReplacement, 1)
err = ioutil.WriteFile("yass.nsi", []byte(nsiContent), 0666)
if err != nil {
glog.Fatalf("%v", err)
}
glog.Info("Feeding NSIS compiler...")
if runtime.GOOS == "windows" {
cmdRun([]string{"makensis.exe", "/XSetCompressor /FINAL lzma", "yass.nsi"}, true)
} else {
cmdRun([]string{"makensis", "-XSetCompressor /FINAL lzma", "yass.nsi"}, true)
}
}
func generateNSISSystemInstaller(output string) {
glog.Info("Feeding CPack NSIS compiler...")
if runtime.GOOS == "windows" {
cmdRun([]string{"cpack.exe"}, true)
} else {
cmdRun([]string{"cpack"}, true)
}
if systemNameFlag == "windows" {
if msvcTargetArchFlag == "x86" {
os.Rename(fmt.Sprintf("yass-%s-win32.exe", tagFlag), output)
} else if msvcTargetArchFlag == "x64" {
os.Rename(fmt.Sprintf("yass-%s-win64.exe", tagFlag), output)
} else if msvcTargetArchFlag == "arm64" {
os.Rename(fmt.Sprintf("yass-%s-win64.exe", tagFlag), output)
} else {
glog.Fatalf("Unsupported msvc arch: %s for nsis builder", msvcTargetArchFlag)
}
} else if systemNameFlag == "mingw" {
if archFlag == "x86" || archFlag == "i686" {
os.Rename(fmt.Sprintf("yass-%s-win32.exe", tagFlag), output)
} else if archFlag == "x64" || archFlag == "x86_64" || archFlag == "amd64" {
os.Rename(fmt.Sprintf("yass-%s-win64.exe", tagFlag), output)
} else if archFlag == "arm64" || archFlag == "aarch64" {
os.Rename(fmt.Sprintf("yass-%s-win64.exe", tagFlag), output)
} else {
glog.Fatalf("Unsupported mingw arch: %s for nsis builder", archFlag)
}
}
}
func generateOpenWrtMakefile(archive string, pkg_version string) {
archive_dir, _ := filepath.Abs("..")
archive_dir += "/"
archive_checksum := strings.TrimSpace(cmdCheckOutput([]string{"sha256sum", archive}))
archive_checksum = strings.Split(archive_checksum, " ")[0]
mkTemplate, err := ioutil.ReadFile(filepath.Join("..", "openwrt", "Makefile.tmpl"))
if err != nil {
glog.Fatalf("%v", err)
}
mkContent := string(mkTemplate)
mkContent = strings.Replace(mkContent, "%%PKG_VERSION%%", pkg_version, 1)
mkContent = strings.Replace(mkContent, "%%PKG_DIR%%", archive_dir, 1)
mkContent = strings.Replace(mkContent, "%%PKG_SHA256SUM%%", archive_checksum, 1)
err = ioutil.WriteFile(filepath.Join("..", "openwrt", "Makefile"), []byte(mkContent), 0666)
if err != nil {
glog.Fatalf("%v", err)
}
glog.Info("OpenWRT Makefile written to openwrt/Makefile")
}
func postStateArchives() map[string][]string {
glog.Info("PostState -- Archives")
glog.Info("======================================================================")
tag := fullTagFlag
var archiveFormat string
if systemNameFlag == "windows" {
osName := "win"
if msvcAllowXpFlag {
if msvcTargetArchFlag == "x86" {
osName = "winxp"
} else {
osName = "win7"
}
}
archiveFormat = fmt.Sprintf("%%s-%s-release-%s-%s-%s%%s%%s", osName, msvcTargetArchFlag, msvcCrtLinkageFlag, tag)
} else if systemNameFlag == "mingw" {
osName := "mingw"
if mingwAllowXpFlag {
if archFlag == "x86" || archFlag == "i686" {
osName = "mingw-winxp"
} else {
osName = "mingw-win7"
}
}
archiveFormat = fmt.Sprintf("%%s-%s-release-%s-%s%%s%%s", osName, archFlag, tag)
} else if systemNameFlag == "darwin" {
osName := "macos"
arch := archFlag
if macosxUniversalBuildFlag {
arch = "universal"
}
archiveFormat = fmt.Sprintf("%%s-%s-release-%s-%s%%s%%s", osName, arch, tag)
} else if systemNameFlag == "freebsd" {
archiveFormat = fmt.Sprintf("%%s-%s%d-release-%s-%s%%s%%s", systemNameFlag, freebsdAbiFlag, archFlag, tag)
} else {
archiveFormat = fmt.Sprintf("%%s-%s-release-%s-%s%%s%%s", systemNameFlag, archFlag, tag)
if subSystemNameFlag != "" {
archiveFormat = fmt.Sprintf("%%s-%s-%s-release-%s-%s%%s%%s", systemNameFlag, subSystemNameFlag, archFlag, tag)
if armCpuFlag != "" {
archiveFormat = fmt.Sprintf("%%s-%s-%s-release-%s-%s-%s%%s%%s", systemNameFlag, subSystemNameFlag, archFlag, armCpuFlag, tag)
}
}
}
ext := ".zip"
if systemNameFlag == "android" || systemNameFlag == "harmony" || systemNameFlag == "linux" || systemNameFlag == "freebsd" {
ext = ".tgz"
}
archive := fmt.Sprintf(archiveFormat, APPNAME, "", ext)
archivePrefix := fmt.Sprintf(archiveFormat, strings.Replace(APPNAME, "_", "-", 1), "", "")
archiveSuffix := fmt.Sprintf(archiveFormat, "", "", "")
archiveSuffix = archiveSuffix[1:]
if systemNameFlag == "android" && variantFlag == "gui" {
if androidAab {
archive = fmt.Sprintf(archiveFormat, APPNAME, "", ".aab")
} else {
archive = fmt.Sprintf(archiveFormat, APPNAME, "", ".apk")
}
}
if systemNameFlag == "darwin" {
archive = fmt.Sprintf(archiveFormat, APPNAME, "-unsigned", ".dmg")
}
if systemNameFlag == "ios" {
archive = fmt.Sprintf(archiveFormat, APPNAME, "", ".ipa")
}
if systemNameFlag == "harmony" {
archive = fmt.Sprintf(archiveFormat, APPNAME, "", ".hap")
}
hasCrashpadExe := true
if _, err := os.Stat("crashpad_handler.exe"); errors.Is(err, os.ErrNotExist) {
hasCrashpadExe = false
}
hasCrashpadPdb := true
if _, err := os.Stat("crashpad_handler.pdb"); errors.Is(err, os.ErrNotExist) {
hasCrashpadPdb = false
}
hasCrashpadDbg := true
if _, err := os.Stat("crashpad_handler.dbg"); errors.Is(err, os.ErrNotExist) {
hasCrashpadDbg = false
}
msiArchive := fmt.Sprintf(archiveFormat, APPNAME, "", ".msi")
nsisArchive := fmt.Sprintf(archiveFormat, APPNAME, "-user-installer", ".exe")
debugArchive := fmt.Sprintf(archiveFormat, APPNAME, "-debuginfo", ext)
nsisSystemArchive := fmt.Sprintf(archiveFormat, APPNAME, "-system-installer", ".exe")
archive = filepath.Join("..", archive)
msiArchive = filepath.Join("..", msiArchive)
nsisArchive = filepath.Join("..", nsisArchive)
debugArchive = filepath.Join("..", debugArchive)
nsisSystemArchive = filepath.Join("..", nsisSystemArchive)
archives := map[string][]string{}
paths := []string{getAppName()}
var dllPaths []string
var dbgPaths []string
if systemNameFlag == "windows" || systemNameFlag == "mingw" {
entries, _ := ioutil.ReadDir("./")
for _, entry := range entries {
name := entry.Name()
iname := strings.ToLower(name)
if strings.HasSuffix(iname, ".dll") {
dllPaths = append(dllPaths, name)
}
}
}
// copying manpages if any
if (variantFlag == "cli" || variantFlag == "server") && (systemNameFlag == "linux" || systemNameFlag == "freebsd") {
paths = append(paths, fmt.Sprintf("../doc/%s.1", APPNAME))
}
// copying dependent crashpad handler if any
if hasCrashpadExe {
paths = append(paths, "crashpad_handler.exe")
}
// copying dependent LICENSEs
licensePaths := postStateArchiveLicenses()
paths = append(paths, licensePaths...)
// main bundle
archiveMainFile(archive, archivePrefix, paths, dllPaths)
archives[archive] = paths
// msi installer
// FIXME wixtoolset3.14 supports arm64 but only 3.11 is out for release
// https://github.com/wixtoolset/issues/issues/5558
// error CNDL0265 : The Platform attribute has an invalid value arm64.
// Possible values are x86, x64, or ia64.
if systemNameFlag == "windows" && msvcTargetArchFlag != "arm64" {
generateMsi(msiArchive, dllPaths, licensePaths, hasCrashpadExe)
archives[msiArchive] = []string{msiArchive}
}
// nsis installer
if systemNameFlag == "windows" || systemNameFlag == "mingw" {
generateNSIS(nsisArchive, dllPaths)
archives[nsisArchive] = []string{nsisArchive}
generateNSISSystemInstaller(nsisSystemArchive)
archives[nsisSystemArchive] = []string{nsisSystemArchive}
}
// debuginfo file
if systemNameFlag == "windows" {
dbgPaths = append(dbgPaths, APPNAME+".pdb")
if hasCrashpadPdb {
dbgPaths = append(dbgPaths, "crashpad_handler.pdb")
}
archiveFiles(debugArchive, archivePrefix, dbgPaths)
} else if systemNameFlag == "mingw" || systemNameFlag == "harmony" || systemNameFlag == "linux" || systemNameFlag == "freebsd" {
dbgPaths = append(dbgPaths, getAppName()+".dbg")
if hasCrashpadDbg {
dbgPaths = append(dbgPaths, "crashpad_handler.dbg")
}
archiveFiles(debugArchive, archivePrefix, dbgPaths)
} else if systemNameFlag == "android" {
// nop because we produces aab now
dbgPaths = []string{}
} else if systemNameFlag == "darwin" {
dbgPaths = append(dbgPaths, getAppName()+".dSYM")
if hasCrashpadDbg {
dbgPaths = append(dbgPaths, "crashpad_handler.dbg")
}
archiveFiles(debugArchive, archivePrefix, dbgPaths)
} else if systemNameFlag == "ios" {
cmdRun([]string{"rm", "-rf", getAppName() + ".dSYM"}, true)
buildSubdir := cmakeBuildTypeFlag + "-iphoneos"
if subSystemNameFlag == "simulator" {
buildSubdir = cmakeBuildTypeFlag + "-iphonesimulator"
}
cmdRun([]string{"rm", "-rf", getAppName() + ".dSYM"}, true)
cmdRun([]string{"rm", "-rf", "YassPacketTunnel.appex.dSYM"}, true)
cmdRun([]string{"ditto", "-v", filepath.Join(buildSubdir, getAppName()+".dSYM"), getAppName() + ".dSYM"}, true)
cmdRun([]string{"ditto", "-v", filepath.Join(buildSubdir, "YassPacketTunnel.appex.dSYM"), "YassPacketTunnel.appex.dSYM"}, true)
archiveFiles(debugArchive, archivePrefix, []string{getAppName() + ".dSYM", "YassPacketTunnel.appex.dSYM"})
dbgPaths = append(dbgPaths, getAppName()+".dSYM", "YassPacketTunnel.appex.dSYM")
}
if len(dbgPaths) > 0 {
archives[debugArchive] = dbgPaths
}
// Create openwrt Makefile
if subSystemNameFlag == "openwrt" && variantFlag == "cli" {
generateOpenWrtMakefile(archive, archiveSuffix)
}
return archives
}
func get7zPath() string {
if runtime.GOOS == "windows" {
return "7z.exe"
} else {
return "7z"
}
}
func inspectArchive(file string, files []string) {
if strings.HasSuffix(file, ".dmg") {
cmdRun([]string{"hdiutil", "imageinfo", file}, false)
} else if strings.HasSuffix(file, ".zip") || strings.HasSuffix(file, ".msi") || strings.HasSuffix(file, ".exe") || strings.HasSuffix(file, ".aab") || strings.HasSuffix(file, ".apk") || strings.HasSuffix(file, ".ipa") || strings.HasSuffix(file, ".hap") {
p7z := get7zPath()
cmdRun([]string{p7z, "l", file}, false)
if strings.HasSuffix(file, ".aab") {
// sign aab file (https://developer.android.com/tools/apksigner)
// FIXME hardcoded with build-tools 35.0.0
apksigner := filepath.Join(androidSdkDir, "build-tools", "35.0.0", "apksigner")
cmdRun([]string{apksigner, "sign", "-v", "--min-sdk-version", fmt.Sprintf("%d", androidApiLevel),
"--ks", getEnv("SIGNING_STORE_PATH", "../android/keystore/debug_keystore.jks"),
"--ks-pass", fmt.Sprintf("pass:%s", getEnv("SIGNING_STORE_PASSWORD", "abc123")),
"--ks-key-alias", getEnv("SIGNING_KEY_ALIAS", "key0"),
"--key-pass", fmt.Sprintf("pass:%s", getEnv("SIGNING_KEY_PASSWORD", "abc123")),
file}, true)
}
if strings.HasSuffix(file, ".apk") || strings.HasSuffix(file, ".aab") {
// check 16kb-alignment with zipalign
// FIXME hardcoded with build-tools 35.0.0
zipalign := filepath.Join(androidSdkDir, "build-tools", "35.0.0", "zipalign")
cmdRun([]string{zipalign, "-c", "-P", "16", "-v", "4", file}, true)
}
if strings.HasSuffix(file, ".apk") {
// verify signature
// FIXME hardcoded with build-tools 35.0.0
apksigner := filepath.Join(androidSdkDir, "build-tools", "35.0.0", "apksigner")
cmdRun([]string{apksigner, "verify", "-v", "--min-sdk-version", fmt.Sprintf("%d", androidApiLevel), file}, true)
}
} else if strings.HasSuffix(file, ".tgz") {
cmdRun([]string{"tar", "tvf", file}, false)
} else {
for _, file := range files {
glog.Infof("------------ %s", file)
}
}
}
func md5sum(src string) (error, string) {
buf := make([]byte, 32768)
fin, err := os.Open(src)
if err != nil {
return err, ""
}
defer func(fin *os.File) {
err := fin.Close()
if err != nil {
glog.Fatalf("%v", err)
}
}(fin)
hash := md5.New()
for {
n, err := fin.Read(buf)
if err != nil && err != io.EOF {
return err, ""
}
if n == 0 {
break
}
hash.Write(buf[:n])
}
return nil, hex.EncodeToString(hash.Sum(nil))
}
func sha1sum(src string) (error, string) {
buf := make([]byte, 32768)
fin, err := os.Open(src)
if err != nil {
return err, ""
}
defer func(fin *os.File) {
err := fin.Close()
if err != nil {
glog.Fatalf("%v", err)
}
}(fin)
hash := sha1.New()
for {
n, err := fin.Read(buf)
if err != nil && err != io.EOF {
return err, ""
}
if n == 0 {
break
}
hash.Write(buf[:n])
}
return nil, hex.EncodeToString(hash.Sum(nil))
}
func sha256sum(src string) (error, string) {
buf := make([]byte, 32768)
fin, err := os.Open(src)
if err != nil {
return err, ""
}
defer func(fin *os.File) {
err := fin.Close()
if err != nil {
glog.Fatalf("%v", err)
}
}(fin)
hash := sha256.New()
for {
n, err := fin.Read(buf)
if err != nil && err != io.EOF {
return err, ""
}
if n == 0 {
break
}
hash.Write(buf[:n])
}
return nil, hex.EncodeToString(hash.Sum(nil))
}
func sha512sum(src string) (error, string) {
buf := make([]byte, 32768)
fin, err := os.Open(src)
if err != nil {
return err, ""
}
defer func(fin *os.File) {
err := fin.Close()
if err != nil {
glog.Fatalf("%v", err)
}
}(fin)
hash := sha512.New()
for {
n, err := fin.Read(buf)
if err != nil && err != io.EOF {
return err, ""
}
if n == 0 {
break
}
hash.Write(buf[:n])
}
return nil, hex.EncodeToString(hash.Sum(nil))
}
func postStateInspectArchives(archives map[string][]string) {
glog.Info("PostState -- Inspect Archives")
glog.Info("======================================================================")
for archive := range archives {
glog.Infof("------ %s:", filepath.Base(archive))
glog.Infof("======================================================================")
inspectArchive(archive, archives[archive])
}
glog.Info("md5sum")
glog.Info("======================================================================")
for archive := range archives {
err, md5sum := md5sum(archive)
if err != nil {
glog.Fatalf("%v", err)
}
glog.Infof("%s\t%s", md5sum, filepath.Base(archive))
}
glog.Info("sha1sum")
glog.Info("======================================================================")
for archive := range archives {
err, sha1sum := sha1sum(archive)
if err != nil {
glog.Fatalf("%v", err)
}
glog.Infof("%s\t%s", sha1sum, filepath.Base(archive))
}
glog.Info("sha256sum")
glog.Info("======================================================================")
for archive := range archives {
err, sha256sum := sha256sum(archive)
if err != nil {
glog.Fatalf("%v", err)
}
glog.Infof("%s\t%s", sha256sum, filepath.Base(archive))
}
glog.Info("sha512sum")
glog.Info("======================================================================")
for archive := range archives {
err, sha512sum := sha512sum(archive)
if err != nil {
glog.Fatalf("%v", err)
}
glog.Infof("%s\t%s", sha512sum, filepath.Base(archive))
}
}
func main() {
InitFlag()
// PreStage Find Source Directory
prebuildFindSourceDirectory()
// BuildStage Generate Build Script
buildStageGenerateBuildScript()
if noConfigureFlag || noBuildFlag {
return
}
// BuildStage Execute Build Script
buildStageExecuteBuildScript()
if noPackagingFlag {
return
}
// PostStage Copy depended libraries
postStateCopyDependedLibraries()
// PostStage Fix RPath
postStateFixRPath()
// PostStage Strip Binaries
postStateStripBinaries()
// PostStage Code Sign
postStateCodeSign()
// PostStage Check Universal Build
postCheckUniversalBuild()
// PostStage Archive
archives := postStateArchives()
// PostStage Inspect Archive
postStateInspectArchives(archives)
}