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