func digestFile(filePath string) (string, error) {
	f, err := os.Open(filePath)
	if err != nil {
		return "", fmt.Errorf("unable to open file %q: %w", filePath, err)
	}
	defer f.Close()
	h := sha256.New()
	if _, err := io.Copy(h, f); err != nil {
		return "", fmt.Errorf("error while hashing the contents of %q: %w", filePath, err)
	}
	return hex.EncodeToString(h.Sum(nil)), nil
}
digestFile returns the sha256 digest of the contents of the given file.
package rbeconfigsgen
import (
	"archive/tar"
	"bytes"
	"crypto/sha256"
	_ "embed"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"text/template"
	"time"
	"github.com/coreos/go-semver/semver"
)
const (
	buildHeader = `# Copyright 2020 The Bazel Authors. All rights reserved.
#
# 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: 
#
# 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.
# This file is auto-generated by github.com/bazelbuild/bazel-toolchains/pkg/rbeconfigsgen
# and should not be modified directly.
`
)
var (
	platformsToolchainBuildTemplate = template.Must(template.New("platformsBuild").Parse(buildHeader + `
package(default_visibility = ["//visibility:public"])
{{ if .CppToolchainTarget }}
toolchain(
    name = "cc-toolchain",
    exec_compatible_with = [
{{ range .ExecConstraints }}        "{{ . }}",
{{ end }}    ],
    target_compatible_with = [
{{ range .TargetConstraints }}        "{{ . }}",
{{ end }}    ],
    toolchain = "{{ .CppToolchainTarget }}",
    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
){{ end }}
platform(
    name = "platform",
    parents = ["@local_config_platform//:host"],
    constraint_values = [
{{ range .ExecConstraints }}        "{{ . }}",
{{ end }}    ],
    exec_properties = {
        "container-image": "docker://{{.ToolchainContainer}}",
        "OSFamily": "{{.OSFamily}}",
    },
)
`))
	legacyJavaBuildTemplate = template.Must(template.New("javaBuild").Parse(buildHeader + `
package(default_visibility = ["//visibility:public"])
java_runtime(
    name = "jdk",
    srcs = [],
    java_home = "{{ .JavaHome }}",
)
`))
	javaBuildTemplate = template.Must(template.New("javaBuild").Parse(buildHeader + `
load("@bazel_tools//tools/jdk:local_java_repository.bzl", "local_java_runtime")
package(default_visibility = ["//visibility:public"])
alias(
    name = "jdk",
    actual = "rbe_jdk",
)
local_java_runtime(
    name = "rbe_jdk",
    java_home = "{{ .JavaHome }}",
    version = "{{ .JavaVersion }}",
)
`))
	imageDigestRegexp = regexp.MustCompile("sha256:([a-f0-9]{64})$")
	licenseBlob []byte
)
type PlatformToolchainsTemplateParams struct {
	ExecConstraints    []string
	TargetConstraints  []string
	CppToolchainTarget string
	ToolchainContainer string
	OSFamily           string
}
func (p PlatformToolchainsTemplateParams) String() string {
	return fmt.Sprintf("{ExecConstraints: %v, TargetConstraints: %v, CppToolchainTarget: %q, ToolchainContainer: %q, OSFamily: %q}",
		p.ExecConstraints, p.TargetConstraints, p.CppToolchainTarget, p.ToolchainContainer, p.OSFamily)
}
type javaBuildTemplateParams struct {
	JavaHome    string
	JavaVersion string
}
type dockerRunner struct {
	containerImage string
	stopContainer bool
	workdir string
	env []string
	dockerPath string
	containerID string
	resolvedImage string
}
type generatedFile struct {
	name     string
	contents []byte
}
type outputConfigs struct {
	license generatedFile
	cppConfigsTarball string
	configBuild generatedFile
	javaBuild generatedFile
}
func runCmd(cmd string, args ...string) (string, error) {
	cmdStr := fmt.Sprintf("'%s'", strings.Join(append([]string{cmd}, args...), " "))
	log.Printf("Running: %s", cmdStr)
	c := exec.Command(cmd, args...)
	o, err := c.CombinedOutput()
	if err != nil {
		log.Printf("Output: %s", o)
		return "", err
	}
	return string(o), nil
}
func workdir(os string) string {
	switch os {
	case OSLinux:
		return "/workdir"
	case OSWindows:
		return "C:/workdir"
	}
	log.Fatalf("Invalid OS: %q", os)
	return ""
}
func BazeliskDownloadInfo(os string) (string, string, error) {
	switch os {
	case OSLinux:
		return "https://github.com/bazelbuild/bazelisk/releases/download/v1.10.1/bazelisk-linux-amd64", "bazelisk", nil
	case OSWindows:
		return "https://github.com/bazelbuild/bazelisk/releases/download/v1.10.1/bazelisk-windows-amd64.exe", "bazelisk.exe", nil
	}
	return "", "", fmt.Errorf("invalid OS %q", os)
}
func newDockerRunner(containerImage string, stopContainer bool) (*dockerRunner, error) {
	if containerImage == "" {
		return nil, fmt.Errorf("container image was not specified")
	}
	d := &dockerRunner{
		containerImage: containerImage,
		stopContainer:  stopContainer,
		dockerPath:     "docker",
	}
	if _, err := runCmd(d.dockerPath, "pull", d.containerImage); err != nil {
		return nil, fmt.Errorf("docker was unable to pull the toolchain container image %q: %w", d.containerImage, err)
	}
	resolvedImage, err := runCmd(d.dockerPath, "inspect", "--format={{index .RepoDigests 0}}", d.containerImage)
	if err != nil {
		return nil, fmt.Errorf("failed to convert toolchain container image %q into a fully qualified image name by digest: %w", d.containerImage, err)
	}
	resolvedImage = strings.TrimSpace(resolvedImage)
	log.Printf("Resolved toolchain image %q to fully qualified reference %q.", d.containerImage, resolvedImage)
	d.resolvedImage = resolvedImage
	cid, err := runCmd(d.dockerPath, "create", "--rm", d.resolvedImage, "sleep", "infinity")
	if err != nil {
		return nil, fmt.Errorf("failed to create a container with the toolchain container image: %w", err)
	}
	cid = strings.TrimSpace(cid)
	if len(cid) != 64 {
		return nil, fmt.Errorf("container ID %q extracted from the stdout of the container create command had unexpected length, got %d, want 64", cid, len(cid))
	}
	d.containerID = cid
	log.Printf("Created container ID %v for toolchain container image %v.", d.containerID, d.resolvedImage)
	if _, err := runCmd(d.dockerPath, "start", d.containerID); err != nil {
		return nil, fmt.Errorf("failed to run the toolchain container: %w", err)
	}
	return d, nil
}
func (d *dockerRunner) execCmd(args ...string) (string, error) {
	a := []string{"exec"}
	if d.workdir != "" {
		a = append(a, "-w", d.workdir)
	}
	for _, e := range d.env {
		a = append(a, "-e", e)
	}
	a = append(a, d.containerID)
	a = append(a, args...)
	o, err := runCmd(d.dockerPath, a...)
	return strings.TrimSpace(o), err
}
func (d *dockerRunner) cleanup() {
	if !d.stopContainer {
		log.Printf("Not stopping container %v of image %v because the Cleanup option was set to false.", d.containerID, d.resolvedImage)
		return
	}
	if _, err := runCmd(d.dockerPath, "stop", "-t", "0", d.containerID); err != nil {
		log.Printf("Failed to stop container %v of toolchain image %v but it's ok to ignore this error if config generation & extraction succeeded.", d.containerID, d.resolvedImage)
	}
}
func (d *dockerRunner) copyToContainer(src, dst string) error {
	if _, err := runCmd(d.dockerPath, "cp", src, fmt.Sprintf("%s:%s", d.containerID, dst)); err != nil {
		return err
	}
	return nil
}
func (d *dockerRunner) copyFromContainer(src, dst string) error {
	if _, err := runCmd(d.dockerPath, "cp", fmt.Sprintf("%s:%s", d.containerID, src), dst); err != nil {
		return err
	}
	return nil
}
func (d *dockerRunner) getEnv() (map[string]string, error) {
	result := make(map[string]string)
	o, err := runCmd(d.dockerPath, "inspect", "-f", "{{range $i, $v := .Config.Env}}{{println $v}}{{end}}", d.resolvedImage)
	if err != nil {
		return nil, fmt.Errorf("failed to inspect the docker image to get environment variables: %w", err)
	}
	split := strings.Split(o, "\n")
	for _, s := range split {
		s = strings.TrimSpace(s)
		if len(s) == 0 {
			continue
		}
		keyVal := strings.SplitN(s, "=", 2)
		key := ""
		val := ""
		if len(keyVal) == 2 {
			key, val = keyVal[0], keyVal[1]
		} else if len(keyVal) == 1 {
			key = keyVal[0]
		}
		if len(key) == 0 {
			continue
		}
		result[key] = val
	}
	return result, nil
}
func installBazelisk(d *dockerRunner, downloadDir, execOS string) (string, error) {
	url, filename, err := BazeliskDownloadInfo(execOS)
	if err != nil {
		return "", fmt.Errorf("unable to determine how to download Bazelisk for execution OS %q: %w", execOS, err)
	}
	resp, err := http.Get(url)
	if err != nil {
		return "", fmt.Errorf("unable to initiate download for Bazelisk from %s: %w", url, err)
	}
	defer resp.Body.Close()
	localPath := path.Join(downloadDir, filename)
	o, err := os.Create(localPath)
	if err != nil {
		return "", fmt.Errorf("unable to open a file at %q to download Bazelisk to: %w", localPath, err)
	}
	if _, err := io.Copy(o, resp.Body); err != nil {
		return "", fmt.Errorf("error while downloading Bazelisk to %s: %w", localPath, err)
	}
	bazeliskContainerPath := path.Join(d.workdir, filename)
	if err := d.copyToContainer(localPath, bazeliskContainerPath); err != nil {
		return "", fmt.Errorf("failed to copy the downloaded Bazelisk binary into the container: %w", err)
	}
	if _, err := d.execCmd("chmod", "+x", bazeliskContainerPath); err != nil {
		return "", fmt.Errorf("failed to mark the Bazelisk binary as executable inside the container: %w", err)
	}
	return bazeliskContainerPath, nil
}
func appendCppEnv(env []string, o *Options) ([]string, error) {
	for k, v := range o.CppGenEnv {
		env = append(env, fmt.Sprintf("%s=%s", k, v))
	}
	if len(o.CppGenEnvJSON) == 0 {
		return env, nil
	}
	blob, err := ioutil.ReadFile(o.CppGenEnvJSON)
	if err != nil {
		return nil, fmt.Errorf("unable to read JSON file %q to read C++ config generation environment variables from: %w", o.CppGenEnvJSON, err)
	}
	e := map[string]string{}
	if err := json.Unmarshal(blob, &e); err != nil {
		return nil, fmt.Errorf("unable to parse file %q as a JSON string -> string dictionary: %w", o.CppGenEnvJSON, err)
	}
	for k, v := range e {
		env = append(env, fmt.Sprintf("%s=%s", k, v))
	}
	return env, nil
}
func genCppConfigs(d *dockerRunner, o *Options, bazeliskPath string) (string, error) {
	if !o.GenCPPConfigs {
		return "", nil
	}
	cppProjDir := path.Join(d.workdir, "cpp_configs_project")
	if _, err := d.execCmd("mkdir", cppProjDir); err != nil {
		return "", fmt.Errorf("failed to create empty directory %q inside the toolchain container: %w", cppProjDir, err)
	}
	oldWorkDir := d.workdir
	d.workdir = cppProjDir
	defer func() {
		d.workdir = oldWorkDir
	}()
	if _, err := d.execCmd("touch", "WORKSPACE", "BUILD.bazel"); err != nil {
		return "", fmt.Errorf("failed to create empty build & workspace files in the container to initialize a blank Bazel repository: %w", err)
	}
	oldEnv := d.env
	defer func() {
		d.env = oldEnv
	}()
	bazeliskEnv := []string{fmt.Sprintf("USE_BAZEL_VERSION=%s", o.BazelVersion)}
	generationEnv, err := appendCppEnv(bazeliskEnv, o)
	if err != nil {
		return "", fmt.Errorf("failed to add additional environment variables to the C++ config generation docker command: %w", err)
	}
	d.env = generationEnv
	cmd := []string{
		bazeliskPath,
		o.CppBazelCmd,
	}
	cmd = append(cmd, o.CPPConfigTargets...)
	if _, err := d.execCmd(cmd...); err != nil {
		return "", fmt.Errorf("Bazel was unable to build the C++ config generation targets in the toolchain container: %w", err)
	}
	d.env = bazeliskEnv
	bazelOutputRoot, err := d.execCmd(bazeliskPath, "info", "output_base")
	if err != nil {
		return "", fmt.Errorf("unable to determine the build output directory where Bazel produced C++ configs in the toolchain container: %w", err)
	}
	cppConfigDir := path.Join(bazelOutputRoot, "external", o.CPPConfigRepo)
	log.Printf("Extracting C++ config files generated by Bazel at %q from the toolchain container.", cppConfigDir)
	d.env = oldEnv
	var out string
	if o.ExecOS == "windows" {
		out, err = d.execCmd("cmd", "/r", "dir", filepath.Clean(cppConfigDir), "/a:l", "/b")
	} else {
		out, err = d.execCmd("find", cppConfigDir, "-type", "l")
	}
	if err != nil {
		errMsg := fmt.Sprintf("unable to list symlinks in the C++ config generation build output directory: ")
		switch o.ExecOS {
		case "windows":
			out = ""
			log.Printf("Ignoring error indicating no symlinks were found in the Bazel output directory: %v", err)
		default:
			return "", fmt.Errorf("%s%w", errMsg, err)
		}
	}
	symlinks := strings.Split(out, "\n")
	for _, s := range symlinks {
		if s == "" {
			continue
		}
		resolvedPath, err := d.execCmd("readlink", s)
		if err != nil {
			return "", fmt.Errorf("unable to determine what the symlink %q in %q in the toolchain container points to: %w", s, cppConfigDir, err)
		}
		if _, err := d.execCmd("ln", "-f", resolvedPath, s); err != nil {
			return "", fmt.Errorf("failed to harden symlink %q in %q pointing to %q: %w", s, cppConfigDir, resolvedPath, err)
		}
	}
	outputTarball := "cpp_configs.tar"
	outputTarballPath := path.Join(o.TempWorkDir, outputTarball)
	outputTarballContainerPath := path.Join(cppProjDir, outputTarball)
	if _, err := d.execCmd("tar", "-cf", outputTarballContainerPath, "-C", cppConfigDir, "."); err != nil {
		return "", fmt.Errorf("failed to archive the C++ configs into a tarball inside the toolchain container: %w", err)
	}
	if err := d.copyFromContainer(outputTarballContainerPath, outputTarballPath); err != nil {
		return "", fmt.Errorf("failed to copy the C++ config tarball out of the toolchain container: %w", err)
	}
	log.Printf("Generated C++ configs at %s.", outputTarballPath)
	return outputTarballPath, nil
}
func UsesLocalJavaRuntime(bazelVersion string) (bool, error) {
	bv, err := semver.NewVersion(bazelVersion)
	if err != nil {
		return false, fmt.Errorf("unable to parse Bazel version %q as a semver: %w", bazelVersion, err)
	}
	return !bv.LessThan(*semver.New("5.0.0")), nil
}
func genJavaConfigs(d *dockerRunner, o *Options) (generatedFile, error) {
	if !o.GenJavaConfigs {
		return generatedFile{}, nil
	}
	imageEnv, err := d.getEnv()
	if err != nil {
		return generatedFile{}, fmt.Errorf("unable to get the environment of the toolchain image to determine JAVA_HOME: %w", err)
	}
	javaHome, ok := imageEnv["JAVA_HOME"]
	if !ok {
		return generatedFile{}, fmt.Errorf("toolchain image didn't specify environment value JAVA_HOME")
	}
	if len(javaHome) == 0 {
		return generatedFile{}, fmt.Errorf("the value of the JAVA_HOME environment variable was blank in the toolchain image")
	}
	log.Printf("JAVA_HOME was %q.", javaHome)
	javaBin := path.Join(javaHome, "bin/java")
	out, err := d.execCmd(javaBin, "-XshowSettings:properties", "-version")
	if err != nil {
		return generatedFile{}, fmt.Errorf("unable to determine the Java version installed in the toolchain container: %w", err)
	}
	javaVersion := ""
	for _, line := range strings.Split(out, "\n") {
		splitVersion := strings.SplitN(line, "=", 2)
		if len(splitVersion) != 2 {
			continue
		}
		key := strings.TrimSpace(splitVersion[0])
		val := strings.TrimSpace(splitVersion[1])
		if key != "java.version" {
			continue
		}
		javaVersion = val
	}
	if len(javaVersion) == 0 {
		return generatedFile{}, fmt.Errorf("unable to determine the java version installed in the container by running 'java -XshowSettings:properties' in the container because it didn't return a line that looked like java.version = <version>")
	}
	log.Printf("Java version: '%s'.", javaVersion)
	usesNewJavaRule, err := UsesLocalJavaRuntime(o.BazelVersion)
	if err != nil {
		return generatedFile{}, fmt.Errorf("unable to determine what Java toolchain rule to use for Bazel %q: %w", o.BazelVersion, err)
	}
	t := legacyJavaBuildTemplate
	if usesNewJavaRule {
		t = javaBuildTemplate
	}
	buf := bytes.NewBuffer(nil)
	if err := t.Execute(buf, &javaBuildTemplateParams{
		JavaHome:    javaHome,
		JavaVersion: javaVersion,
	}); err != nil {
		return generatedFile{}, fmt.Errorf("failed to generate the contents of the BUILD file with the Java toolchain definition: %w", err)
	}
	return generatedFile{
		name:     "java/BUILD",
		contents: buf.Bytes(),
	}, nil
}
func processTempDir(o *Options) error {
	if o.TempWorkDir != "" {
		s, err := os.Stat(o.TempWorkDir)
		if err != nil {
			return fmt.Errorf("got %q specified as option TempWorkDir but the path doesn't exist: %w", o.TempWorkDir, err)
		}
		if !s.IsDir() {
			return fmt.Errorf("got %q specified as option TempWorkDir but the path doesn't point to a directory", o.TempWorkDir)
		}
		return nil
	}
	dir, err := ioutil.TempDir("", "rbeconfigsgen_")
	if err != nil {
		return fmt.Errorf("failed to create a temporary local directory to write intermediate files: %w", err)
	}
	o.TempWorkDir = dir
	return nil
}
func genCppToolchainTarget(o *Options) string {
	if o.OutputConfigPath != "" {
		return fmt.Sprintf("//%s/cc:%s", strings.ReplaceAll(o.OutputConfigPath, "\\", "/"), o.CPPToolchainTargetName)
	}
	return fmt.Sprintf("//cc:%s", o.CPPToolchainTargetName)
}
func genConfigBuild(o *Options) (generatedFile, error) {
	if o.PlatformParams.CppToolchainTarget != "" {
		return generatedFile{}, fmt.Errorf("<internal error> C++ toolchain target was already set")
	}
	if o.GenCPPConfigs {
		o.PlatformParams.CppToolchainTarget = genCppToolchainTarget(o)
	} else {
		o.PlatformParams.CppToolchainTarget = ""
		log.Printf("Not generating a toolchain target to be used for the C++ Crosstool top because C++ config generation is disabled.")
	}
	buf := bytes.NewBuffer(nil)
	log.Printf("Fully resolved platform params=%v", o.PlatformParams)
	if err := platformsToolchainBuildTemplate.Execute(buf, o.PlatformParams); err != nil {
		return generatedFile{}, fmt.Errorf("failed to generate platform BUILD file: %w", err)
	}
	return generatedFile{
		name:     "config/BUILD",
		contents: buf.Bytes(),
	}, nil
}
func copyCppConfigsToTarball(inTarPath string, outTar *tar.Writer) error {
	in, err := os.Open(inTarPath)
	if err != nil {
		return fmt.Errorf("unable to open input tarball %q for reading: %w", inTarPath, err)
	}
	defer in.Close()
	inTar := tar.NewReader(in)
	pathPrefix := "cc"
	for {
		h, err := inTar.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return fmt.Errorf("error while reading input tarball %q: %w", inTarPath, err)
		}
		switch h.Typeflag {
		case tar.TypeDir:
			break
		case tar.TypeReg:
			if strings.HasSuffix(h.Name, "WORKSPACE") {
				break
			}
			outH := *h
			outH.Name = path.Join(pathPrefix, h.Name)
			outH.ModTime = time.Unix(0, 0)
			if err := outTar.WriteHeader(&outH); err != nil {
				return fmt.Errorf("error while adding tar header for %q from input tarball to output tarball: %w", h.Name, err)
			}
			if _, err := io.Copy(outTar, inTar); err != nil {
				return fmt.Errorf("failed to copy the contents of %q from intput tarball to the output tarball: %w", h.Name, err)
			}
		default:
			return fmt.Errorf("got unexpected entry with name %q of type %v in tarball %q: %w", h.Name, h.Typeflag, inTarPath, err)
		}
	}
	return nil
}
func writeGeneratedFileToTarball(g generatedFile, outTar *tar.Writer) error {
	if err := outTar.WriteHeader(&tar.Header{
		Name:    g.name,
		Size:    int64(len(g.contents)),
		Mode:    int64(os.ModePerm),
		ModTime: time.Unix(0, 0),
	}); err != nil {
		return fmt.Errorf("failed to write tar header for %q: %w", g.name, err)
	}
	if _, err := io.Copy(outTar, bytes.NewBuffer(g.contents)); err != nil {
		return fmt.Errorf("failed to copy the contents of %q to the output tarball: %w", g.name, err)
	}
	return nil
}
func assembleConfigTarball(o *Options, oc outputConfigs) error {
	out, err := os.Create(o.OutputTarball)
	if err != nil {
		return fmt.Errorf("unable to open output tarball %q for writing: %w", o.OutputTarball, err)
	}
	outTar := tar.NewWriter(out)
	if err := writeGeneratedFileToTarball(oc.license, outTar); err != nil {
		return fmt.Errorf("unable to write the %q file to the output tarball %q: %w", oc.license.name, o.OutputTarball, err)
	}
	if o.GenCPPConfigs {
		if err := copyCppConfigsToTarball(oc.cppConfigsTarball, outTar); err != nil {
			return fmt.Errorf("unable to copy C++ configs from the C++ config tarball %q to the output tarball %q: %w", oc.cppConfigsTarball, o.OutputTarball, err)
		}
	}
	if o.GenJavaConfigs {
		if err := writeGeneratedFileToTarball(oc.javaBuild, outTar); err != nil {
			return fmt.Errorf("unable to write the BUILD file %q containing the Java toolchain definition to the output tarball %q: %w", oc.javaBuild.name, o.OutputTarball, err)
		}
	}
	if err := writeGeneratedFileToTarball(oc.configBuild, outTar); err != nil {
		return fmt.Errorf("unable to write the crosstool top/platform BUILD file %q to the output tarball %q: %w", oc.configBuild.name, o.OutputTarball, err)
	}
	if err := outTar.Close(); err != nil {
		return fmt.Errorf("error trying to finish writing the output tarball %q: %w", o.OutputTarball, err)
	}
	log.Printf("Generated Bazel toolchain configs output tarball %q.", o.OutputTarball)
	return nil
}
func copyCppConfigsToOutputDir(outDir string, cppConfigsTarball string) error {
	in, err := os.Open(cppConfigsTarball)
	if err != nil {
		return fmt.Errorf("unable to open input tarball %q for reading: %w", cppConfigsTarball, err)
	}
	defer in.Close()
	inTar := tar.NewReader(in)
	outDir = path.Join(outDir, "cc")
	for {
		h, err := inTar.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return fmt.Errorf("error while reading input tarball %q: %w", cppConfigsTarball, err)
		}
		if h.Typeflag != tar.TypeReg {
			continue
		}
		filePath := path.Join(outDir, h.Name)
		dirPath := path.Dir(filePath)
		if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
			return fmt.Errorf("unable to create directory %q to extract file %q from the C++ config tarball %q: %w", dirPath, h.Name, cppConfigsTarball, err)
		}
		o, err := os.Create(filePath)
		if err != nil {
			return fmt.Errorf("failed to create file %q for writing %q from the C++ config tarball %q: %w", filePath, h.Name, cppConfigsTarball, err)
		}
		if _, err := io.Copy(o, inTar); err != nil {
			return fmt.Errorf("error while extracting %q from %q to %q: %w", h.Name, cppConfigsTarball, filePath, err)
		}
		o.Close()
	}
	return nil
}
func writeGeneratedFile(outDir string, g generatedFile) error {
	fullPath := path.Join(outDir, g.name)
	dirPath := path.Dir(fullPath)
	if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
		return fmt.Errorf("unable to create directory %q to write %q in directory %q: %w", dirPath, g.name, outDir, err)
	}
	if err := ioutil.WriteFile(fullPath, g.contents, os.ModePerm); err != nil {
		return fmt.Errorf("unable to write file %q: %w", fullPath, err)
	}
	return nil
}
func copyConfigsToOutputDir(o *Options, oc outputConfigs) error {
	configsRootDir := path.Join(o.OutputSourceRoot, o.OutputConfigPath)
	if err := os.MkdirAll(configsRootDir, os.ModePerm); err != nil {
		return fmt.Errorf("unable to create directory %q for writing configs: %w", configsRootDir, err)
	}
	if err := writeGeneratedFile(configsRootDir, oc.license); err != nil {
		return fmt.Errorf("unable to write the %q file to the output directory %q: %w", oc.license.name, configsRootDir, err)
	}
	if o.GenCPPConfigs {
		if err := copyCppConfigsToOutputDir(configsRootDir, oc.cppConfigsTarball); err != nil {
			return fmt.Errorf("unable to extract C++ configs into output directory %q: %w", configsRootDir, err)
		}
	}
	if o.GenJavaConfigs {
		if err := writeGeneratedFile(configsRootDir, oc.javaBuild); err != nil {
			return fmt.Errorf("unable to write Java configs into output directory %q: %w", configsRootDir, err)
		}
	}
	if err := writeGeneratedFile(configsRootDir, oc.configBuild); err != nil {
		return fmt.Errorf("unable to write the crostool top/platform BUILD file into output directory %q: %w", configsRootDir, err)
	}
	log.Printf("Copied generated configs to directory %q.", configsRootDir)
	return nil
}
func assembleConfigs(o *Options, oc outputConfigs) error {
	if len(o.OutputTarball) != 0 {
		if err := assembleConfigTarball(o, oc); err != nil {
			return fmt.Errorf("failed to assemble configs into a tarball: %w", err)
		}
	}
	if len(o.OutputSourceRoot) != 0 {
		if err := copyConfigsToOutputDir(o, oc); err != nil {
			return fmt.Errorf("failed to write configs to directory %q: %w", o.OutputSourceRoot, err)
		}
	}
	return nil
}