• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2020 Google LLC
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//     https://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 workspace
16
17import (
18	"fmt"
19	"io"
20	"os"
21	"path/filepath"
22	"sort"
23	"strings"
24
25	"android.googlesource.com/platform/tools/treble.git/hacksaw/bind"
26	"android.googlesource.com/platform/tools/treble.git/hacksaw/git"
27)
28
29type Composer struct {
30	pathBinder bind.PathBinder
31}
32
33func NewComposer(bm bind.PathBinder) Composer {
34	return Composer{bm}
35}
36
37func isDirEmpty(name string) (bool, error) {
38	dir, err := os.Open(name)
39	if err != nil {
40		return false, err
41	}
42	defer dir.Close()
43	_, err = dir.Readdirnames(1)
44	if err == io.EOF {
45		return true, nil
46	}
47	return false, err
48}
49
50//Compose a workspace from a codebase
51//Returns a list of path binds in the order they
52//were bound
53func (m Composer) Compose(codebasePath string, workspacePath string) ([]string, error) {
54	lister := git.NewRepoLister()
55	gitProjects, err := lister.List(codebasePath)
56	if err != nil {
57		return nil, err
58	}
59	fmt.Print("Composing")
60	var bindList []string
61	//Sorting the list of projects in alphabetical
62	//order ensures that parent projects are bound
63	//before their nested child projects, which is important
64	//to avoid bind conflicts
65	sort.Strings(gitProjects)
66	for _, project := range gitProjects {
67		fmt.Print(".") //Display some progress
68		//skip empty project names
69		if project == "" {
70			continue
71		}
72		source := filepath.Join(codebasePath, project)
73		destination := filepath.Join(workspacePath, project)
74		if err = os.MkdirAll(destination, os.ModePerm); err != nil {
75			fmt.Print("\n")
76			return bindList, err
77		}
78		isEmpty, err := isDirEmpty(destination)
79		if err != nil {
80			return bindList, err
81		}
82		if !isEmpty {
83			// If the destination dir already existed and
84			// was not empty then assume we are recreating
85			// a workspace and the current path already
86			// existed in the workspace
87			continue
88		}
89		if err = m.pathBinder.BindReadOnly(source, destination); err != nil {
90			fmt.Print("\n")
91			return bindList, err
92		}
93		bindList = append(bindList, destination)
94	}
95	fmt.Print("\n")
96	fmt.Println("Workspace composed")
97	copier := NewFileCopier()
98	return bindList, copier.Copy(codebasePath, gitProjects, workspacePath)
99}
100
101//Dismantle a workspace
102//Returns a list of path unbinds in the order they
103//were unbound
104func (m Composer) Dismantle(dismantlePath string) ([]string, error) {
105	bindList, err := m.List(dismantlePath)
106	if err != nil {
107		return nil, err
108	}
109	//Sorting the list of binds in reverse alphabetical
110	//order ensures that nested child projects are unbound
111	//before their parent projects, which is important
112	//to avoid unbind conflicts
113	sort.Sort(sort.Reverse(sort.StringSlice(bindList)))
114	fmt.Print("Dismantling")
115	var unbindList []string
116	for _, bindPath := range bindList {
117		fmt.Print(".") //Display some progress
118		if err = m.pathBinder.Unbind(bindPath); err != nil {
119			fmt.Print("\n")
120			return unbindList, err
121		}
122		unbindList = append(unbindList, bindPath)
123	}
124	fmt.Print("\n")
125	fmt.Println("Workspace dismantled")
126	return unbindList, err
127}
128
129//Unbind a project
130func (m Composer) Unbind(unbindPath string) error {
131	return m.pathBinder.Unbind(unbindPath)
132}
133
134//List all binds attached under a directory
135func (m Composer) List(listPath string) ([]string, error) {
136	listPath, err := filepath.EvalSymlinks(listPath)
137	if err != nil {
138		return nil, err
139	}
140	fullBindList, err := m.pathBinder.List()
141	if err != nil {
142		return nil, err
143	}
144	var matchBindList []string
145	for _, bindPath := range fullBindList {
146		if strings.HasPrefix(bindPath+"/", listPath+"/") {
147			matchBindList = append(matchBindList, bindPath)
148		}
149	}
150	return matchBindList, err
151}
152