xref: /aosp_15_r20/tools/treble/hacksaw/workspace/compose.go (revision 105f628577ac4ba0e277a494fbb614ed8c12a994)
1*105f6285SAndroid Build Coastguard Worker// Copyright 2020 Google LLC
2*105f6285SAndroid Build Coastguard Worker//
3*105f6285SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*105f6285SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*105f6285SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*105f6285SAndroid Build Coastguard Worker//
7*105f6285SAndroid Build Coastguard Worker//     https://www.apache.org/licenses/LICENSE-2.0
8*105f6285SAndroid Build Coastguard Worker//
9*105f6285SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*105f6285SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*105f6285SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*105f6285SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*105f6285SAndroid Build Coastguard Worker// limitations under the License.
14*105f6285SAndroid Build Coastguard Worker
15*105f6285SAndroid Build Coastguard Workerpackage workspace
16*105f6285SAndroid Build Coastguard Worker
17*105f6285SAndroid Build Coastguard Workerimport (
18*105f6285SAndroid Build Coastguard Worker	"fmt"
19*105f6285SAndroid Build Coastguard Worker	"io"
20*105f6285SAndroid Build Coastguard Worker	"os"
21*105f6285SAndroid Build Coastguard Worker	"path/filepath"
22*105f6285SAndroid Build Coastguard Worker	"sort"
23*105f6285SAndroid Build Coastguard Worker	"strings"
24*105f6285SAndroid Build Coastguard Worker
25*105f6285SAndroid Build Coastguard Worker	"android.googlesource.com/platform/tools/treble.git/hacksaw/bind"
26*105f6285SAndroid Build Coastguard Worker	"android.googlesource.com/platform/tools/treble.git/hacksaw/git"
27*105f6285SAndroid Build Coastguard Worker)
28*105f6285SAndroid Build Coastguard Worker
29*105f6285SAndroid Build Coastguard Workertype Composer struct {
30*105f6285SAndroid Build Coastguard Worker	pathBinder bind.PathBinder
31*105f6285SAndroid Build Coastguard Worker}
32*105f6285SAndroid Build Coastguard Worker
33*105f6285SAndroid Build Coastguard Workerfunc NewComposer(bm bind.PathBinder) Composer {
34*105f6285SAndroid Build Coastguard Worker	return Composer{bm}
35*105f6285SAndroid Build Coastguard Worker}
36*105f6285SAndroid Build Coastguard Worker
37*105f6285SAndroid Build Coastguard Workerfunc isDirEmpty(name string) (bool, error) {
38*105f6285SAndroid Build Coastguard Worker	dir, err := os.Open(name)
39*105f6285SAndroid Build Coastguard Worker	if err != nil {
40*105f6285SAndroid Build Coastguard Worker		return false, err
41*105f6285SAndroid Build Coastguard Worker	}
42*105f6285SAndroid Build Coastguard Worker	defer dir.Close()
43*105f6285SAndroid Build Coastguard Worker	_, err = dir.Readdirnames(1)
44*105f6285SAndroid Build Coastguard Worker	if err == io.EOF {
45*105f6285SAndroid Build Coastguard Worker		return true, nil
46*105f6285SAndroid Build Coastguard Worker	}
47*105f6285SAndroid Build Coastguard Worker	return false, err
48*105f6285SAndroid Build Coastguard Worker}
49*105f6285SAndroid Build Coastguard Worker
50*105f6285SAndroid Build Coastguard Worker//Compose a workspace from a codebase
51*105f6285SAndroid Build Coastguard Worker//Returns a list of path binds in the order they
52*105f6285SAndroid Build Coastguard Worker//were bound
53*105f6285SAndroid Build Coastguard Workerfunc (m Composer) Compose(codebasePath string, workspacePath string) ([]string, error) {
54*105f6285SAndroid Build Coastguard Worker	lister := git.NewRepoLister()
55*105f6285SAndroid Build Coastguard Worker	gitProjects, err := lister.List(codebasePath)
56*105f6285SAndroid Build Coastguard Worker	if err != nil {
57*105f6285SAndroid Build Coastguard Worker		return nil, err
58*105f6285SAndroid Build Coastguard Worker	}
59*105f6285SAndroid Build Coastguard Worker	fmt.Print("Composing")
60*105f6285SAndroid Build Coastguard Worker	var bindList []string
61*105f6285SAndroid Build Coastguard Worker	//Sorting the list of projects in alphabetical
62*105f6285SAndroid Build Coastguard Worker	//order ensures that parent projects are bound
63*105f6285SAndroid Build Coastguard Worker	//before their nested child projects, which is important
64*105f6285SAndroid Build Coastguard Worker	//to avoid bind conflicts
65*105f6285SAndroid Build Coastguard Worker	sort.Strings(gitProjects)
66*105f6285SAndroid Build Coastguard Worker	for _, project := range gitProjects {
67*105f6285SAndroid Build Coastguard Worker		fmt.Print(".") //Display some progress
68*105f6285SAndroid Build Coastguard Worker		//skip empty project names
69*105f6285SAndroid Build Coastguard Worker		if project == "" {
70*105f6285SAndroid Build Coastguard Worker			continue
71*105f6285SAndroid Build Coastguard Worker		}
72*105f6285SAndroid Build Coastguard Worker		source := filepath.Join(codebasePath, project)
73*105f6285SAndroid Build Coastguard Worker		destination := filepath.Join(workspacePath, project)
74*105f6285SAndroid Build Coastguard Worker		if err = os.MkdirAll(destination, os.ModePerm); err != nil {
75*105f6285SAndroid Build Coastguard Worker			fmt.Print("\n")
76*105f6285SAndroid Build Coastguard Worker			return bindList, err
77*105f6285SAndroid Build Coastguard Worker		}
78*105f6285SAndroid Build Coastguard Worker		isEmpty, err := isDirEmpty(destination)
79*105f6285SAndroid Build Coastguard Worker		if err != nil {
80*105f6285SAndroid Build Coastguard Worker			return bindList, err
81*105f6285SAndroid Build Coastguard Worker		}
82*105f6285SAndroid Build Coastguard Worker		if !isEmpty {
83*105f6285SAndroid Build Coastguard Worker			// If the destination dir already existed and
84*105f6285SAndroid Build Coastguard Worker			// was not empty then assume we are recreating
85*105f6285SAndroid Build Coastguard Worker			// a workspace and the current path already
86*105f6285SAndroid Build Coastguard Worker			// existed in the workspace
87*105f6285SAndroid Build Coastguard Worker			continue
88*105f6285SAndroid Build Coastguard Worker		}
89*105f6285SAndroid Build Coastguard Worker		if err = m.pathBinder.BindReadOnly(source, destination); err != nil {
90*105f6285SAndroid Build Coastguard Worker			fmt.Print("\n")
91*105f6285SAndroid Build Coastguard Worker			return bindList, err
92*105f6285SAndroid Build Coastguard Worker		}
93*105f6285SAndroid Build Coastguard Worker		bindList = append(bindList, destination)
94*105f6285SAndroid Build Coastguard Worker	}
95*105f6285SAndroid Build Coastguard Worker	fmt.Print("\n")
96*105f6285SAndroid Build Coastguard Worker	fmt.Println("Workspace composed")
97*105f6285SAndroid Build Coastguard Worker	copier := NewFileCopier()
98*105f6285SAndroid Build Coastguard Worker	return bindList, copier.Copy(codebasePath, gitProjects, workspacePath)
99*105f6285SAndroid Build Coastguard Worker}
100*105f6285SAndroid Build Coastguard Worker
101*105f6285SAndroid Build Coastguard Worker//Dismantle a workspace
102*105f6285SAndroid Build Coastguard Worker//Returns a list of path unbinds in the order they
103*105f6285SAndroid Build Coastguard Worker//were unbound
104*105f6285SAndroid Build Coastguard Workerfunc (m Composer) Dismantle(dismantlePath string) ([]string, error) {
105*105f6285SAndroid Build Coastguard Worker	bindList, err := m.List(dismantlePath)
106*105f6285SAndroid Build Coastguard Worker	if err != nil {
107*105f6285SAndroid Build Coastguard Worker		return nil, err
108*105f6285SAndroid Build Coastguard Worker	}
109*105f6285SAndroid Build Coastguard Worker	//Sorting the list of binds in reverse alphabetical
110*105f6285SAndroid Build Coastguard Worker	//order ensures that nested child projects are unbound
111*105f6285SAndroid Build Coastguard Worker	//before their parent projects, which is important
112*105f6285SAndroid Build Coastguard Worker	//to avoid unbind conflicts
113*105f6285SAndroid Build Coastguard Worker	sort.Sort(sort.Reverse(sort.StringSlice(bindList)))
114*105f6285SAndroid Build Coastguard Worker	fmt.Print("Dismantling")
115*105f6285SAndroid Build Coastguard Worker	var unbindList []string
116*105f6285SAndroid Build Coastguard Worker	for _, bindPath := range bindList {
117*105f6285SAndroid Build Coastguard Worker		fmt.Print(".") //Display some progress
118*105f6285SAndroid Build Coastguard Worker		if err = m.pathBinder.Unbind(bindPath); err != nil {
119*105f6285SAndroid Build Coastguard Worker			fmt.Print("\n")
120*105f6285SAndroid Build Coastguard Worker			return unbindList, err
121*105f6285SAndroid Build Coastguard Worker		}
122*105f6285SAndroid Build Coastguard Worker		unbindList = append(unbindList, bindPath)
123*105f6285SAndroid Build Coastguard Worker	}
124*105f6285SAndroid Build Coastguard Worker	fmt.Print("\n")
125*105f6285SAndroid Build Coastguard Worker	fmt.Println("Workspace dismantled")
126*105f6285SAndroid Build Coastguard Worker	return unbindList, err
127*105f6285SAndroid Build Coastguard Worker}
128*105f6285SAndroid Build Coastguard Worker
129*105f6285SAndroid Build Coastguard Worker//Unbind a project
130*105f6285SAndroid Build Coastguard Workerfunc (m Composer) Unbind(unbindPath string) error {
131*105f6285SAndroid Build Coastguard Worker	return m.pathBinder.Unbind(unbindPath)
132*105f6285SAndroid Build Coastguard Worker}
133*105f6285SAndroid Build Coastguard Worker
134*105f6285SAndroid Build Coastguard Worker//List all binds attached under a directory
135*105f6285SAndroid Build Coastguard Workerfunc (m Composer) List(listPath string) ([]string, error) {
136*105f6285SAndroid Build Coastguard Worker	listPath, err := filepath.EvalSymlinks(listPath)
137*105f6285SAndroid Build Coastguard Worker	if err != nil {
138*105f6285SAndroid Build Coastguard Worker		return nil, err
139*105f6285SAndroid Build Coastguard Worker	}
140*105f6285SAndroid Build Coastguard Worker	fullBindList, err := m.pathBinder.List()
141*105f6285SAndroid Build Coastguard Worker	if err != nil {
142*105f6285SAndroid Build Coastguard Worker		return nil, err
143*105f6285SAndroid Build Coastguard Worker	}
144*105f6285SAndroid Build Coastguard Worker	var matchBindList []string
145*105f6285SAndroid Build Coastguard Worker	for _, bindPath := range fullBindList {
146*105f6285SAndroid Build Coastguard Worker		if strings.HasPrefix(bindPath+"/", listPath+"/") {
147*105f6285SAndroid Build Coastguard Worker			matchBindList = append(matchBindList, bindPath)
148*105f6285SAndroid Build Coastguard Worker		}
149*105f6285SAndroid Build Coastguard Worker	}
150*105f6285SAndroid Build Coastguard Worker	return matchBindList, err
151*105f6285SAndroid Build Coastguard Worker}
152