xref: /aosp_15_r20/external/bazelbuild-rules_go/go/tools/builders/replicate.go (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
1*9bb1b549SSpandan Das// Copyright 2018 The Bazel Authors. All rights reserved.
2*9bb1b549SSpandan Das//
3*9bb1b549SSpandan Das// Licensed under the Apache License, Version 2.0 (the "License");
4*9bb1b549SSpandan Das// you may not use this file except in compliance with the License.
5*9bb1b549SSpandan Das// You may obtain a copy of the License at
6*9bb1b549SSpandan Das//
7*9bb1b549SSpandan Das//    http://www.apache.org/licenses/LICENSE-2.0
8*9bb1b549SSpandan Das//
9*9bb1b549SSpandan Das// Unless required by applicable law or agreed to in writing, software
10*9bb1b549SSpandan Das// distributed under the License is distributed on an "AS IS" BASIS,
11*9bb1b549SSpandan Das// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9bb1b549SSpandan Das// See the License for the specific language governing permissions and
13*9bb1b549SSpandan Das// limitations under the License.
14*9bb1b549SSpandan Das
15*9bb1b549SSpandan Das// stdlib builds the standard library in the appropriate mode into a new goroot.
16*9bb1b549SSpandan Daspackage main
17*9bb1b549SSpandan Das
18*9bb1b549SSpandan Dasimport (
19*9bb1b549SSpandan Das	"fmt"
20*9bb1b549SSpandan Das	"io"
21*9bb1b549SSpandan Das	"os"
22*9bb1b549SSpandan Das	"path/filepath"
23*9bb1b549SSpandan Das)
24*9bb1b549SSpandan Das
25*9bb1b549SSpandan Dastype replicateMode int
26*9bb1b549SSpandan Das
27*9bb1b549SSpandan Dasconst (
28*9bb1b549SSpandan Das	copyMode replicateMode = iota
29*9bb1b549SSpandan Das	hardlinkMode
30*9bb1b549SSpandan Das	softlinkMode
31*9bb1b549SSpandan Das)
32*9bb1b549SSpandan Das
33*9bb1b549SSpandan Dastype replicateOption func(*replicateConfig)
34*9bb1b549SSpandan Dastype replicateConfig struct {
35*9bb1b549SSpandan Das	removeFirst bool
36*9bb1b549SSpandan Das	fileMode    replicateMode
37*9bb1b549SSpandan Das	dirMode     replicateMode
38*9bb1b549SSpandan Das	paths       []string
39*9bb1b549SSpandan Das}
40*9bb1b549SSpandan Das
41*9bb1b549SSpandan Dasfunc replicatePaths(paths ...string) replicateOption {
42*9bb1b549SSpandan Das	return func(config *replicateConfig) {
43*9bb1b549SSpandan Das		config.paths = append(config.paths, paths...)
44*9bb1b549SSpandan Das	}
45*9bb1b549SSpandan Das}
46*9bb1b549SSpandan Das
47*9bb1b549SSpandan Das// replicatePrepare is the common preparation steps for a replication entry
48*9bb1b549SSpandan Dasfunc replicatePrepare(dst string, config *replicateConfig) error {
49*9bb1b549SSpandan Das	dir := filepath.Dir(dst)
50*9bb1b549SSpandan Das	if err := os.MkdirAll(dir, 0755); err != nil {
51*9bb1b549SSpandan Das		return fmt.Errorf("Failed to make %s: %v", dir, err)
52*9bb1b549SSpandan Das	}
53*9bb1b549SSpandan Das	if config.removeFirst {
54*9bb1b549SSpandan Das		_ = os.Remove(dst)
55*9bb1b549SSpandan Das	}
56*9bb1b549SSpandan Das	return nil
57*9bb1b549SSpandan Das}
58*9bb1b549SSpandan Das
59*9bb1b549SSpandan Das// replicateFile is called internally by replicate to map a single file from src into dst.
60*9bb1b549SSpandan Dasfunc replicateFile(src, dst string, config *replicateConfig) error {
61*9bb1b549SSpandan Das	if err := replicatePrepare(dst, config); err != nil {
62*9bb1b549SSpandan Das		return err
63*9bb1b549SSpandan Das	}
64*9bb1b549SSpandan Das	switch config.fileMode {
65*9bb1b549SSpandan Das	case copyMode:
66*9bb1b549SSpandan Das		in, err := os.Open(src)
67*9bb1b549SSpandan Das		if err != nil {
68*9bb1b549SSpandan Das			return err
69*9bb1b549SSpandan Das		}
70*9bb1b549SSpandan Das		defer in.Close()
71*9bb1b549SSpandan Das		out, err := os.Create(dst)
72*9bb1b549SSpandan Das		if err != nil {
73*9bb1b549SSpandan Das			return err
74*9bb1b549SSpandan Das		}
75*9bb1b549SSpandan Das		_, err = io.Copy(out, in)
76*9bb1b549SSpandan Das		closeerr := out.Close()
77*9bb1b549SSpandan Das		if err != nil {
78*9bb1b549SSpandan Das			return err
79*9bb1b549SSpandan Das		}
80*9bb1b549SSpandan Das		if closeerr != nil {
81*9bb1b549SSpandan Das			return closeerr
82*9bb1b549SSpandan Das		}
83*9bb1b549SSpandan Das		s, err := os.Stat(src)
84*9bb1b549SSpandan Das		if err != nil {
85*9bb1b549SSpandan Das			return err
86*9bb1b549SSpandan Das		}
87*9bb1b549SSpandan Das		if err := os.Chmod(dst, s.Mode()); err != nil {
88*9bb1b549SSpandan Das			return err
89*9bb1b549SSpandan Das		}
90*9bb1b549SSpandan Das		return nil
91*9bb1b549SSpandan Das	case hardlinkMode:
92*9bb1b549SSpandan Das		return os.Link(src, dst)
93*9bb1b549SSpandan Das	case softlinkMode:
94*9bb1b549SSpandan Das		return os.Symlink(src, dst)
95*9bb1b549SSpandan Das	default:
96*9bb1b549SSpandan Das		return fmt.Errorf("Invalid replication mode %d", config.fileMode)
97*9bb1b549SSpandan Das	}
98*9bb1b549SSpandan Das}
99*9bb1b549SSpandan Das
100*9bb1b549SSpandan Das// replicateDir makes a tree of files visible in a new location.
101*9bb1b549SSpandan Das// It is allowed to take any efficient method of doing so.
102*9bb1b549SSpandan Dasfunc replicateDir(src, dst string, config *replicateConfig) error {
103*9bb1b549SSpandan Das	if err := replicatePrepare(dst, config); err != nil {
104*9bb1b549SSpandan Das		return err
105*9bb1b549SSpandan Das	}
106*9bb1b549SSpandan Das	switch config.dirMode {
107*9bb1b549SSpandan Das	case copyMode:
108*9bb1b549SSpandan Das		return filepath.Walk(src, func(path string, f os.FileInfo, err error) error {
109*9bb1b549SSpandan Das			if f.IsDir() {
110*9bb1b549SSpandan Das				return nil
111*9bb1b549SSpandan Das			}
112*9bb1b549SSpandan Das			relative, err := filepath.Rel(src, path)
113*9bb1b549SSpandan Das			if err != nil {
114*9bb1b549SSpandan Das				return err
115*9bb1b549SSpandan Das			}
116*9bb1b549SSpandan Das			return replicateFile(path, filepath.Join(dst, relative), config)
117*9bb1b549SSpandan Das		})
118*9bb1b549SSpandan Das	case hardlinkMode:
119*9bb1b549SSpandan Das		return os.Link(src, dst)
120*9bb1b549SSpandan Das	case softlinkMode:
121*9bb1b549SSpandan Das		return os.Symlink(src, dst)
122*9bb1b549SSpandan Das	default:
123*9bb1b549SSpandan Das		return fmt.Errorf("Invalid replication mode %d", config.fileMode)
124*9bb1b549SSpandan Das	}
125*9bb1b549SSpandan Das}
126*9bb1b549SSpandan Das
127*9bb1b549SSpandan Das// replicateTree is called for each single src dst pair.
128*9bb1b549SSpandan Dasfunc replicateTree(src, dst string, config *replicateConfig) error {
129*9bb1b549SSpandan Das	if err := os.RemoveAll(dst); err != nil {
130*9bb1b549SSpandan Das		return fmt.Errorf("Failed to remove file at destination %s: %v", dst, err)
131*9bb1b549SSpandan Das	}
132*9bb1b549SSpandan Das	if l, err := filepath.EvalSymlinks(src); err != nil {
133*9bb1b549SSpandan Das		return err
134*9bb1b549SSpandan Das	} else {
135*9bb1b549SSpandan Das		src = l
136*9bb1b549SSpandan Das	}
137*9bb1b549SSpandan Das	if s, err := os.Stat(src); err != nil {
138*9bb1b549SSpandan Das		return err
139*9bb1b549SSpandan Das	} else if s.IsDir() {
140*9bb1b549SSpandan Das		return replicateDir(src, dst, config)
141*9bb1b549SSpandan Das	}
142*9bb1b549SSpandan Das	return replicateFile(src, dst, config)
143*9bb1b549SSpandan Das}
144*9bb1b549SSpandan Das
145*9bb1b549SSpandan Das// replicate makes a tree of files visible in a new location.
146*9bb1b549SSpandan Das// You control how it does so using options, by default it presumes the entire tree
147*9bb1b549SSpandan Das// of files rooted at src must be visible at dst, and that it should do so by copying.
148*9bb1b549SSpandan Das// src is allowed to be a file, in which case just the one file is copied.
149*9bb1b549SSpandan Dasfunc replicate(src, dst string, options ...replicateOption) error {
150*9bb1b549SSpandan Das	config := replicateConfig{
151*9bb1b549SSpandan Das		removeFirst: true,
152*9bb1b549SSpandan Das	}
153*9bb1b549SSpandan Das	for _, option := range options {
154*9bb1b549SSpandan Das		option(&config)
155*9bb1b549SSpandan Das	}
156*9bb1b549SSpandan Das	if len(config.paths) == 0 {
157*9bb1b549SSpandan Das		return replicateTree(src, dst, &config)
158*9bb1b549SSpandan Das	}
159*9bb1b549SSpandan Das	for _, base := range config.paths {
160*9bb1b549SSpandan Das		from := filepath.Join(src, base)
161*9bb1b549SSpandan Das		to := filepath.Join(dst, base)
162*9bb1b549SSpandan Das		if err := replicateTree(from, to, &config); err != nil {
163*9bb1b549SSpandan Das			return err
164*9bb1b549SSpandan Das		}
165*9bb1b549SSpandan Das	}
166*9bb1b549SSpandan Das	return nil
167*9bb1b549SSpandan Das}
168