How to use syscall.SysProcAttr struct fields for Windows when OS is set for Linux

Viewed 2324

I'm working on code in Go that will install a program. It has to run Linux and Windows systems. I am using Intellij.

    if isWindows() {
        cmd := exec.Command("msiexec.exe", "/i", filename, "/quiet", "L*V", installLog)
        cmd.SysProcAttr = &syscall.SysProcAttr{
            CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
        }
        if err := cmd.Start(); err != nil {
            log.Printf(failedInstallMsg, filename, err)
            return
        }
    } else if isLinux() {
            cmd := exec.Command("sudo", "-n", packageManagerTool, "--disablerepo=*", "install", "-y", filename)
            cmd.SysProcAttr = &syscall.SysProcAttr{
                Setpgid: true,
                Pgid: 0,
            }
            if err := cmd.Start(); err != nil {
                log.Printf(failedInstallMsg, filename, err)
                return
            }
        }
    }

My issue is that since in the IDE Go Project settings > Build tags the OS is set to Linux then the syscall.SysProcAttr struct fields are only available for Linux. This means the line in the windows case that initialize the struct with

        cmd.SysProcAttr = &syscall.SysProcAttr{
            CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
        }

causes a compilation error

unknown field CreationFlags in struct literal

I tried adding

// +build linux windows

At the top of the file but it doesn't help.

Is there an easy way to have code for both windows and linux co-exist in the way described above?

1 Answers

In case you haven't figured this out, you can put them in separate files, and then use either build tags or file

// main.go
package main

func main() {
    // some stuff
    forkProcess()
    // some other things
}
// fork_windows.go
// +build windows

package main

import (
    "log"
    "os/exec"
    "syscall"
)

func forkProcess(packageManagerTool, filename, installLog, failedInstallMsg string) {
    cmd := exec.Command("msiexec.exe", "/i", filename, "/quiet", "L*V", installLog)
    cmd.SysProcAttr = &syscall.SysProcAttr{
        CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
    }
    if err := cmd.Start(); err != nil {
        log.Printf(failedInstallMsg, filename, err)
    }
}
// fork_linux.go
// +build linux

package main

import (
    "log"
    "os/exec"
    "syscall"
)

func forkProcess(packageManagerTool, filename, installLog, failedInstallMsg string) {
    cmd := exec.Command("sudo", "-n", packageManagerTool, "--disablerepo=*", "install", "-y", filename)
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Setpgid: true,
        Pgid: 0,
    }
    if err := cmd.Start(); err != nil {
        log.Printf(failedInstallMsg, filename, err)
        return
    }
}

Note: you can either forego the _windows and _linux suffixes in the files, or the build tags (but not both). Build tags are useful if you wanna target more platforms in a single file (e.g. a fork.go with a build tag of // +build linux,freebsd,darwin, and another fork_other.go with tag of // +build windows

More info on build constraints can be found on the golang docs

Related