| // Copyright 2017 The Fuchsia Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | package main | 
 |  | 
 | import ( | 
 | 	"archive/tar" | 
 | 	"bufio" | 
 | 	"flag" | 
 | 	"fmt" | 
 | 	"io" | 
 | 	"log" | 
 | 	"os" | 
 | 	"path/filepath" | 
 | 	"strings" | 
 | ) | 
 |  | 
 | var output = flag.String("output", "", "Path to the generated tarball") | 
 | var manifest = flag.String("manifest", "", "Path to the file containing a description of the tarball's contents") | 
 |  | 
 | func archiveFile(tw *tar.Writer, src, dest string) error { | 
 | 	file, err := os.Open(src) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 | 	defer file.Close() | 
 | 	info, err := file.Stat() | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	hdr, err := tar.FileInfoHeader(info, info.Name()) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 | 	hdr.Name = dest | 
 | 	hdr.Uid = 0 | 
 | 	hdr.Gid = 0 | 
 | 	if err := tw.WriteHeader(hdr); err != nil { | 
 | 		return err | 
 | 	} | 
 | 	_, err = io.Copy(tw, file) | 
 | 	return err | 
 | } | 
 |  | 
 | func archiveDirectory(tw *tar.Writer, src, dest string) error { | 
 | 	return filepath.Walk(src, func(path string, info os.FileInfo, err error) error { | 
 | 		if err != nil { | 
 | 			return err | 
 | 		} | 
 | 		if info.IsDir() { | 
 | 			return nil | 
 | 		} | 
 | 		fileDest := filepath.Join(dest, path[len(src)+1:]) | 
 | 		return archiveFile(tw, path, fileDest) | 
 | 	}) | 
 | } | 
 |  | 
 | func createTar(archive string, mappings map[string]string) error { | 
 | 	file, err := os.Create(archive) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 | 	defer file.Close() | 
 |  | 
 | 	w := NewWriter(file) | 
 | 	defer w.Close() | 
 | 	tw := tar.NewWriter(w) | 
 | 	defer tw.Close() | 
 |  | 
 | 	for dest, src := range mappings { | 
 | 		info, err := os.Stat(src) | 
 | 		if err != nil { | 
 | 			return err | 
 | 		} | 
 | 		if info.IsDir() { | 
 | 			err = archiveDirectory(tw, src, dest) | 
 | 		} else { | 
 | 			err = archiveFile(tw, src, dest) | 
 | 		} | 
 | 		if err != nil { | 
 | 			return err | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return nil | 
 | } | 
 |  | 
 | func readManifest(manifest string) (map[string]string, error) { | 
 | 	file, err := os.Open(manifest) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	defer file.Close() | 
 |  | 
 | 	result := make(map[string]string) | 
 |  | 
 | 	scanner := bufio.NewScanner(file) | 
 | 	for scanner.Scan() { | 
 | 		parts := strings.SplitN(scanner.Text(), "=", 2) | 
 | 		result[parts[0]] = parts[1] | 
 | 	} | 
 |  | 
 | 	if err := scanner.Err(); err != nil { | 
 | 		return nil, err | 
 | 	} | 
 |  | 
 | 	return result, nil | 
 | } | 
 |  | 
 | func generateArchive(manifest string, output string) error { | 
 | 	if _, err := os.Stat(output); err == nil { | 
 | 		// The file exists, need to remove it first. | 
 | 		if err := os.Remove(output); err != nil { | 
 | 			return err | 
 | 		} | 
 | 	} | 
 | 	mappings, err := readManifest(manifest) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 | 	return createTar(output, mappings) | 
 | } | 
 |  | 
 | func main() { | 
 | 	flag.Usage = func() { | 
 | 		fmt.Fprintf(os.Stderr, `Usage: tar_maker --manifest path/to/manifest | 
 |  | 
 | This tool creates a tarball whose contents are described in a file manifest. | 
 | Each line in the manifest should be of the form: | 
 |     <path in tarball>=<path to source file or dir> | 
 |  | 
 | The tarball is compressed using gzip. | 
 | `) | 
 | 		flag.PrintDefaults() | 
 | 	} | 
 | 	flag.Parse() | 
 |  | 
 | 	if *output == "" { | 
 | 		flag.Usage() | 
 | 		log.Fatalf("Error: missing -output flag.") | 
 | 	} | 
 | 	if *manifest == "" { | 
 | 		flag.Usage() | 
 | 		log.Fatalf("Error: missing -manifest flag.") | 
 | 	} | 
 |  | 
 | 	if err := generateArchive(*manifest, *output); err != nil { | 
 | 		log.Fatalf("Error: unable to create archive %s: %v", *output, err) | 
 | 	} | 
 | } |