xref: /aosp_15_r20/external/bazelbuild-rules_go/go/tools/builders/go_path.go (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
1// Copyright 2018 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package main
16
17import (
18	"archive/zip"
19	"encoding/json"
20	"errors"
21	"flag"
22	"fmt"
23	"io"
24	"io/ioutil"
25	"log"
26	"os"
27	"path/filepath"
28)
29
30type mode int
31
32const (
33	invalidMode mode = iota
34	archiveMode
35	copyMode
36	linkMode
37)
38
39func modeFromString(s string) (mode, error) {
40	switch s {
41	case "archive":
42		return archiveMode, nil
43	case "copy":
44		return copyMode, nil
45	case "link":
46		return linkMode, nil
47	default:
48		return invalidMode, fmt.Errorf("invalid mode: %s", s)
49	}
50}
51
52type manifestEntry struct {
53	Src, Dst string
54}
55
56func main() {
57	log.SetPrefix("GoPath: ")
58	log.SetFlags(0)
59	if err := run(os.Args[1:]); err != nil {
60		log.Fatal(err)
61	}
62}
63
64func run(args []string) error {
65	var manifest, out string
66	flags := flag.NewFlagSet("go_path", flag.ContinueOnError)
67	flags.StringVar(&manifest, "manifest", "", "name of json file listing files to include")
68	flags.StringVar(&out, "out", "", "output file or directory")
69	modeFlag := flags.String("mode", "", "copy, link, or archive")
70	if err := flags.Parse(args); err != nil {
71		return err
72	}
73	if manifest == "" {
74		return errors.New("-manifest not set")
75	}
76	if out == "" {
77		return errors.New("-out not set")
78	}
79	if *modeFlag == "" {
80		return errors.New("-mode not set")
81	}
82	mode, err := modeFromString(*modeFlag)
83	if err != nil {
84		return err
85	}
86
87	entries, err := readManifest(manifest)
88	if err != nil {
89		return err
90	}
91
92	switch mode {
93	case archiveMode:
94		err = archivePath(out, entries)
95	case copyMode:
96		err = copyPath(out, entries)
97	case linkMode:
98		err = linkPath(out, entries)
99	}
100	return err
101}
102
103func readManifest(path string) ([]manifestEntry, error) {
104	data, err := ioutil.ReadFile(path)
105	if err != nil {
106		return nil, fmt.Errorf("error reading manifest: %v", err)
107	}
108	var entries []manifestEntry
109	if err := json.Unmarshal(data, &entries); err != nil {
110		return nil, fmt.Errorf("error unmarshalling manifest %s: %v", path, err)
111	}
112	return entries, nil
113}
114
115func archivePath(out string, manifest []manifestEntry) (err error) {
116	outFile, err := os.Create(out)
117	if err != nil {
118		return err
119	}
120	defer func() {
121		if e := outFile.Close(); err == nil && e != nil {
122			err = fmt.Errorf("error closing archive %s: %v", out, e)
123		}
124	}()
125	outZip := zip.NewWriter(outFile)
126
127	for _, entry := range manifest {
128		srcFile, err := os.Open(abs(filepath.FromSlash(entry.Src)))
129		if err != nil {
130			return err
131		}
132		w, err := outZip.Create(entry.Dst)
133		if err != nil {
134			srcFile.Close()
135			return err
136		}
137		if _, err := io.Copy(w, srcFile); err != nil {
138			srcFile.Close()
139			return err
140		}
141		if err := srcFile.Close(); err != nil {
142			return err
143		}
144	}
145
146	if err := outZip.Close(); err != nil {
147		return fmt.Errorf("error constructing archive %s: %v", out, err)
148	}
149	return nil
150}
151
152func copyPath(out string, manifest []manifestEntry) error {
153	if err := os.MkdirAll(out, 0777); err != nil {
154		return err
155	}
156	for _, entry := range manifest {
157		dst := abs(filepath.Join(out, filepath.FromSlash(entry.Dst)))
158		if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil {
159			return err
160		}
161		srcFile, err := os.Open(abs(filepath.FromSlash(entry.Src)))
162		if err != nil {
163			return err
164		}
165		dstFile, err := os.Create(dst)
166		if err != nil {
167			srcFile.Close()
168			return err
169		}
170		if _, err := io.Copy(dstFile, srcFile); err != nil {
171			dstFile.Close()
172			srcFile.Close()
173			return err
174		}
175		srcFile.Close()
176		if err := dstFile.Close(); err != nil {
177			return err
178		}
179	}
180	return nil
181}
182
183func linkPath(out string, manifest []manifestEntry) error {
184	// out directory may already exist and may contain old symlinks. Delete.
185	if err := os.RemoveAll(out); err != nil {
186		return err
187	}
188	if err := os.MkdirAll(out, 0777); err != nil {
189		return err
190	}
191	for _, entry := range manifest {
192		dst := filepath.Join(out, filepath.FromSlash(entry.Dst))
193		dstDir := filepath.Dir(dst)
194		src, _ := filepath.Rel(dstDir, entry.Src)
195		if err := os.MkdirAll(dstDir, 0777); err != nil {
196			return err
197		}
198		if err := os.Symlink(src, dst); err != nil {
199			return err
200		}
201	}
202	return nil
203}
204