tools/build_gcsfuse/main.go (145 lines of code) (raw):

// Copyright 2015 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // A tool that builds gcsfuse binaries, assembles helper scripts, etc., and // writes them out to a destination directory with the correct hierarchy. // // Usage: // // build_gcsfuse src_dir dst_dir version // // where src_dir is the root of the gcsfuse git repository (or a tarball // thereof). // // For Linux, writes the following to dst_dir: // // bin/gcsfuse // sbin/mount.fuse.gcsfuse // sbin/mount.gcsfuse // // For OS X: // // bin/gcsfuse // sbin/mount_gcsfuse package main import ( "flag" "fmt" "log" "os" "os/exec" "path" "runtime" "strings" ) // Build release binaries according to the supplied settings, setting up the // the file system structure we desire (see package-level comments). // // version is the gcsfuse version being built (e.g. "0.11.1"), or a short git // commit name if this is not for an official release. func buildBinaries(dstDir, srcDir, version string, buildArgs []string) (err error) { osys := runtime.GOOS // Create the target structure. { dirs := []string{ "bin", "sbin", } for _, d := range dirs { err = os.Mkdir(path.Join(dstDir, d), 0755) if err != nil { err = fmt.Errorf("mkdir: %w", err) return } } } pathEnv, exists := os.LookupEnv("PATH") if !exists { err = fmt.Errorf("$PATH not found in OS") return } // Create a directory to become GOPATH for our build below. gopath, err := os.MkdirTemp("", "build_gcsfuse_gopath") if err != nil { err = fmt.Errorf("TempDir: %w", err) return } defer os.RemoveAll(gopath) // Create a directory to become GOCACHE for our build below. var gocache string gocache, err = os.MkdirTemp("", "build_gcsfuse_gocache") if err != nil { err = fmt.Errorf("TempDir: %w", err) return } defer os.RemoveAll(gocache) // Make it appear as if the source directory is at the appropriate position // in $GOPATH. gcsfuseDir := path.Join(gopath, "src/github.com/googlecloudplatform/gcsfuse") err = os.MkdirAll(path.Dir(gcsfuseDir), 0700) if err != nil { err = fmt.Errorf("MkdirAll: %w", err) return } err = os.Symlink(srcDir, gcsfuseDir) if err != nil { err = fmt.Errorf("symlink: %w", err) return } // mount(8) expects a different name format on Linux. mountHelperName := "mount_gcsfuse" if osys == "linux" { mountHelperName = "mount.gcsfuse" } // Build the binaries. binaries := []struct { goTarget string outputPath string }{ { "github.com/googlecloudplatform/gcsfuse/v2", "bin/gcsfuse", }, { "github.com/googlecloudplatform/gcsfuse/v2/tools/mount_gcsfuse", path.Join("sbin", mountHelperName), }, } for _, bin := range binaries { log.Printf("Building %s to %s", bin.goTarget, bin.outputPath) // Set up arguments. cmd := exec.Command( "go", "build", "-C", srcDir, "-o", path.Join(dstDir, bin.outputPath)) if path.Base(bin.outputPath) == "gcsfuse" { cmd.Args = append( cmd.Args, "-ldflags", fmt.Sprintf("-X github.com/googlecloudplatform/gcsfuse/v2/common.gcsfuseVersion=%s", version), ) cmd.Args = append(cmd.Args, buildArgs...) } cmd.Args = append(cmd.Args, bin.goTarget) // Set up environment. cmd.Env = append( os.Environ(), "GO15VENDOREXPERIMENT=1", "GO111MODULE=auto", fmt.Sprintf("PATH=%s", pathEnv), fmt.Sprintf("GOROOT=%s", runtime.GOROOT()), fmt.Sprintf("GOPATH=%s", gopath), fmt.Sprintf("GOCACHE=%s", gocache), "CGO_ENABLED=0", ) // Build. var output []byte output, err = cmd.CombinedOutput() if err != nil { err = fmt.Errorf("%v: %w\nOutput:\n%s", cmd, err, output) if strings.Contains(string(output), "flag provided but not defined: -C") { err = fmt.Errorf("%v: %w\nOutput:\n%s\nPlease upgrade to go version 1.20 or higher", cmd, err, output) } return } } // On Linux, also support `mount -t fuse.gcsfuse`. If there's no explicit // helper for this type, /sbin/mount.fuse will call the gcsfuse executable // directly, but it doesn't support the right argument format. So we install // an explicit helper. if osys == "linux" { err = os.Symlink("mount.gcsfuse", path.Join(dstDir, "sbin/mount.fuse.gcsfuse")) if err != nil { err = fmt.Errorf("symlink: %w", err) return } } return } func run() (err error) { // Extract arguments. args := flag.Args() if len(args) < 3 { err = fmt.Errorf("usage: %s src_dir dst_dir version [build args]", os.Args[0]) return } srcDir := args[0] dstDir := args[1] version := args[2] buildArgs := args[3:] // Build. err = buildBinaries(dstDir, srcDir, version, buildArgs) if err != nil { err = fmt.Errorf("buildBinaries: %w", err) return } return } func main() { log.SetFlags(log.Lmicroseconds) flag.Parse() err := run() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }