xref: /aosp_15_r20/build/soong/cmd/zipsync/zipsync.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker// Copyright 2018 Google Inc. All rights reserved.
2*333d2b36SAndroid Build Coastguard Worker//
3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*333d2b36SAndroid Build Coastguard Worker//
7*333d2b36SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*333d2b36SAndroid Build Coastguard Worker//
9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*333d2b36SAndroid Build Coastguard Worker// limitations under the License.
14*333d2b36SAndroid Build Coastguard Worker
15*333d2b36SAndroid Build Coastguard Workerpackage main
16*333d2b36SAndroid Build Coastguard Worker
17*333d2b36SAndroid Build Coastguard Workerimport (
18*333d2b36SAndroid Build Coastguard Worker	"archive/zip"
19*333d2b36SAndroid Build Coastguard Worker	"flag"
20*333d2b36SAndroid Build Coastguard Worker	"fmt"
21*333d2b36SAndroid Build Coastguard Worker	"io"
22*333d2b36SAndroid Build Coastguard Worker	"io/ioutil"
23*333d2b36SAndroid Build Coastguard Worker	"log"
24*333d2b36SAndroid Build Coastguard Worker	"os"
25*333d2b36SAndroid Build Coastguard Worker	"path/filepath"
26*333d2b36SAndroid Build Coastguard Worker	"strings"
27*333d2b36SAndroid Build Coastguard Worker)
28*333d2b36SAndroid Build Coastguard Worker
29*333d2b36SAndroid Build Coastguard Workervar (
30*333d2b36SAndroid Build Coastguard Worker	outputDir  = flag.String("d", "", "output dir")
31*333d2b36SAndroid Build Coastguard Worker	outputFile = flag.String("l", "", "output list file")
32*333d2b36SAndroid Build Coastguard Worker	zipPrefix  = flag.String("zip-prefix", "", "optional prefix within the zip file to extract, stripping the prefix")
33*333d2b36SAndroid Build Coastguard Worker	filter     multiFlag
34*333d2b36SAndroid Build Coastguard Worker)
35*333d2b36SAndroid Build Coastguard Worker
36*333d2b36SAndroid Build Coastguard Workerfunc init() {
37*333d2b36SAndroid Build Coastguard Worker	flag.Var(&filter, "f", "optional filter pattern")
38*333d2b36SAndroid Build Coastguard Worker}
39*333d2b36SAndroid Build Coastguard Worker
40*333d2b36SAndroid Build Coastguard Workerfunc must(err error) {
41*333d2b36SAndroid Build Coastguard Worker	if err != nil {
42*333d2b36SAndroid Build Coastguard Worker		log.Fatal(err)
43*333d2b36SAndroid Build Coastguard Worker	}
44*333d2b36SAndroid Build Coastguard Worker}
45*333d2b36SAndroid Build Coastguard Worker
46*333d2b36SAndroid Build Coastguard Workerfunc writeFile(filename string, in io.Reader, perm os.FileMode) error {
47*333d2b36SAndroid Build Coastguard Worker	out, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
48*333d2b36SAndroid Build Coastguard Worker	if err != nil {
49*333d2b36SAndroid Build Coastguard Worker		return err
50*333d2b36SAndroid Build Coastguard Worker	}
51*333d2b36SAndroid Build Coastguard Worker	_, err = io.Copy(out, in)
52*333d2b36SAndroid Build Coastguard Worker	if err != nil {
53*333d2b36SAndroid Build Coastguard Worker		out.Close()
54*333d2b36SAndroid Build Coastguard Worker		return err
55*333d2b36SAndroid Build Coastguard Worker	}
56*333d2b36SAndroid Build Coastguard Worker
57*333d2b36SAndroid Build Coastguard Worker	return out.Close()
58*333d2b36SAndroid Build Coastguard Worker}
59*333d2b36SAndroid Build Coastguard Worker
60*333d2b36SAndroid Build Coastguard Workerfunc writeSymlink(filename string, in io.Reader) error {
61*333d2b36SAndroid Build Coastguard Worker	b, err := ioutil.ReadAll(in)
62*333d2b36SAndroid Build Coastguard Worker	if err != nil {
63*333d2b36SAndroid Build Coastguard Worker		return err
64*333d2b36SAndroid Build Coastguard Worker	}
65*333d2b36SAndroid Build Coastguard Worker	dest := string(b)
66*333d2b36SAndroid Build Coastguard Worker	err = os.Symlink(dest, filename)
67*333d2b36SAndroid Build Coastguard Worker	return err
68*333d2b36SAndroid Build Coastguard Worker}
69*333d2b36SAndroid Build Coastguard Worker
70*333d2b36SAndroid Build Coastguard Workerfunc main() {
71*333d2b36SAndroid Build Coastguard Worker	flag.Usage = func() {
72*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintln(os.Stderr, "usage: zipsync -d <output dir> [-l <output file>] [-f <pattern>] [zip]...")
73*333d2b36SAndroid Build Coastguard Worker		flag.PrintDefaults()
74*333d2b36SAndroid Build Coastguard Worker	}
75*333d2b36SAndroid Build Coastguard Worker
76*333d2b36SAndroid Build Coastguard Worker	flag.Parse()
77*333d2b36SAndroid Build Coastguard Worker
78*333d2b36SAndroid Build Coastguard Worker	if *outputDir == "" {
79*333d2b36SAndroid Build Coastguard Worker		flag.Usage()
80*333d2b36SAndroid Build Coastguard Worker		os.Exit(1)
81*333d2b36SAndroid Build Coastguard Worker	}
82*333d2b36SAndroid Build Coastguard Worker
83*333d2b36SAndroid Build Coastguard Worker	inputs := flag.Args()
84*333d2b36SAndroid Build Coastguard Worker
85*333d2b36SAndroid Build Coastguard Worker	// For now, just wipe the output directory and replace its contents with the zip files
86*333d2b36SAndroid Build Coastguard Worker	// Eventually this could only modify the directory contents as necessary to bring it up
87*333d2b36SAndroid Build Coastguard Worker	// to date with the zip files.
88*333d2b36SAndroid Build Coastguard Worker	must(os.RemoveAll(*outputDir))
89*333d2b36SAndroid Build Coastguard Worker
90*333d2b36SAndroid Build Coastguard Worker	must(os.MkdirAll(*outputDir, 0777))
91*333d2b36SAndroid Build Coastguard Worker
92*333d2b36SAndroid Build Coastguard Worker	var files []string
93*333d2b36SAndroid Build Coastguard Worker	seen := make(map[string]string)
94*333d2b36SAndroid Build Coastguard Worker
95*333d2b36SAndroid Build Coastguard Worker	if *zipPrefix != "" {
96*333d2b36SAndroid Build Coastguard Worker		*zipPrefix = filepath.Clean(*zipPrefix) + "/"
97*333d2b36SAndroid Build Coastguard Worker	}
98*333d2b36SAndroid Build Coastguard Worker
99*333d2b36SAndroid Build Coastguard Worker	for _, input := range inputs {
100*333d2b36SAndroid Build Coastguard Worker		reader, err := zip.OpenReader(input)
101*333d2b36SAndroid Build Coastguard Worker		if err != nil {
102*333d2b36SAndroid Build Coastguard Worker			log.Fatal(err)
103*333d2b36SAndroid Build Coastguard Worker		}
104*333d2b36SAndroid Build Coastguard Worker		defer reader.Close()
105*333d2b36SAndroid Build Coastguard Worker
106*333d2b36SAndroid Build Coastguard Worker		for _, f := range reader.File {
107*333d2b36SAndroid Build Coastguard Worker			name := f.Name
108*333d2b36SAndroid Build Coastguard Worker			if *zipPrefix != "" {
109*333d2b36SAndroid Build Coastguard Worker				if !strings.HasPrefix(name, *zipPrefix) {
110*333d2b36SAndroid Build Coastguard Worker					continue
111*333d2b36SAndroid Build Coastguard Worker				}
112*333d2b36SAndroid Build Coastguard Worker				name = strings.TrimPrefix(name, *zipPrefix)
113*333d2b36SAndroid Build Coastguard Worker			}
114*333d2b36SAndroid Build Coastguard Worker
115*333d2b36SAndroid Build Coastguard Worker			if filter != nil {
116*333d2b36SAndroid Build Coastguard Worker				if match, err := filter.Match(filepath.Base(name)); err != nil {
117*333d2b36SAndroid Build Coastguard Worker					log.Fatal(err)
118*333d2b36SAndroid Build Coastguard Worker				} else if !match {
119*333d2b36SAndroid Build Coastguard Worker					continue
120*333d2b36SAndroid Build Coastguard Worker				}
121*333d2b36SAndroid Build Coastguard Worker			}
122*333d2b36SAndroid Build Coastguard Worker
123*333d2b36SAndroid Build Coastguard Worker			if filepath.IsAbs(name) {
124*333d2b36SAndroid Build Coastguard Worker				log.Fatalf("%q in %q is an absolute path", name, input)
125*333d2b36SAndroid Build Coastguard Worker			}
126*333d2b36SAndroid Build Coastguard Worker
127*333d2b36SAndroid Build Coastguard Worker			if prev, exists := seen[name]; exists {
128*333d2b36SAndroid Build Coastguard Worker				log.Fatalf("%q found in both %q and %q", name, prev, input)
129*333d2b36SAndroid Build Coastguard Worker			}
130*333d2b36SAndroid Build Coastguard Worker			seen[name] = input
131*333d2b36SAndroid Build Coastguard Worker
132*333d2b36SAndroid Build Coastguard Worker			filename := filepath.Join(*outputDir, name)
133*333d2b36SAndroid Build Coastguard Worker			if f.FileInfo().IsDir() {
134*333d2b36SAndroid Build Coastguard Worker				must(os.MkdirAll(filename, 0777))
135*333d2b36SAndroid Build Coastguard Worker			} else {
136*333d2b36SAndroid Build Coastguard Worker				must(os.MkdirAll(filepath.Dir(filename), 0777))
137*333d2b36SAndroid Build Coastguard Worker				in, err := f.Open()
138*333d2b36SAndroid Build Coastguard Worker				if err != nil {
139*333d2b36SAndroid Build Coastguard Worker					log.Fatal(err)
140*333d2b36SAndroid Build Coastguard Worker				}
141*333d2b36SAndroid Build Coastguard Worker				if f.FileInfo().Mode()&os.ModeSymlink != 0 {
142*333d2b36SAndroid Build Coastguard Worker					must(writeSymlink(filename, in))
143*333d2b36SAndroid Build Coastguard Worker				} else {
144*333d2b36SAndroid Build Coastguard Worker					must(writeFile(filename, in, f.FileInfo().Mode()))
145*333d2b36SAndroid Build Coastguard Worker				}
146*333d2b36SAndroid Build Coastguard Worker				in.Close()
147*333d2b36SAndroid Build Coastguard Worker				files = append(files, filename)
148*333d2b36SAndroid Build Coastguard Worker			}
149*333d2b36SAndroid Build Coastguard Worker		}
150*333d2b36SAndroid Build Coastguard Worker	}
151*333d2b36SAndroid Build Coastguard Worker
152*333d2b36SAndroid Build Coastguard Worker	if *outputFile != "" {
153*333d2b36SAndroid Build Coastguard Worker		data := strings.Join(files, "\n")
154*333d2b36SAndroid Build Coastguard Worker		if len(files) > 0 {
155*333d2b36SAndroid Build Coastguard Worker			data += "\n"
156*333d2b36SAndroid Build Coastguard Worker		}
157*333d2b36SAndroid Build Coastguard Worker		must(ioutil.WriteFile(*outputFile, []byte(data), 0666))
158*333d2b36SAndroid Build Coastguard Worker	}
159*333d2b36SAndroid Build Coastguard Worker}
160*333d2b36SAndroid Build Coastguard Worker
161*333d2b36SAndroid Build Coastguard Workertype multiFlag []string
162*333d2b36SAndroid Build Coastguard Worker
163*333d2b36SAndroid Build Coastguard Workerfunc (m *multiFlag) String() string {
164*333d2b36SAndroid Build Coastguard Worker	return strings.Join(*m, " ")
165*333d2b36SAndroid Build Coastguard Worker}
166*333d2b36SAndroid Build Coastguard Worker
167*333d2b36SAndroid Build Coastguard Workerfunc (m *multiFlag) Set(s string) error {
168*333d2b36SAndroid Build Coastguard Worker	*m = append(*m, s)
169*333d2b36SAndroid Build Coastguard Worker	return nil
170*333d2b36SAndroid Build Coastguard Worker}
171*333d2b36SAndroid Build Coastguard Worker
172*333d2b36SAndroid Build Coastguard Workerfunc (m *multiFlag) Match(s string) (bool, error) {
173*333d2b36SAndroid Build Coastguard Worker	if m == nil {
174*333d2b36SAndroid Build Coastguard Worker		return false, nil
175*333d2b36SAndroid Build Coastguard Worker	}
176*333d2b36SAndroid Build Coastguard Worker	for _, f := range *m {
177*333d2b36SAndroid Build Coastguard Worker		if match, err := filepath.Match(f, s); err != nil {
178*333d2b36SAndroid Build Coastguard Worker			return false, err
179*333d2b36SAndroid Build Coastguard Worker		} else if match {
180*333d2b36SAndroid Build Coastguard Worker			return true, nil
181*333d2b36SAndroid Build Coastguard Worker		}
182*333d2b36SAndroid Build Coastguard Worker	}
183*333d2b36SAndroid Build Coastguard Worker	return false, nil
184*333d2b36SAndroid Build Coastguard Worker}
185