2 * Copyright (C) 2017-2018 "IoT.bzh"
3 * Author Sebastien Douheret <sebastien@iot.bzh>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
27 common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/golib"
28 "gerrit.automotivelinux.org/gerrit/src/xds/xds-server/lib/xsapiv1"
31 // SDKs List of installed SDK
34 Sdks map[string]*CrossSDK
35 SdksFamilies map[string]*xsapiv1.SDKFamilyConfig
38 stop chan struct{} // signals intentional stop
41 // NewSDKs creates a new instance of SDKs
42 func NewSDKs(ctx *Context) (*SDKs, error) {
45 Sdks: make(map[string]*CrossSDK),
46 SdksFamilies: make(map[string]*xsapiv1.SDKFamilyConfig),
47 stop: make(chan struct{}),
50 scriptsDir := ctx.Config.FileConf.SdkScriptsDir
51 if !common.Exists(scriptsDir) {
52 // allow to use scripts/sdk in debug mode
53 scriptsDir = filepath.Join(filepath.Dir(ctx.Config.FileConf.SdkScriptsDir), "scripts", "sdks")
54 if !common.Exists(scriptsDir) {
55 return &s, fmt.Errorf("scripts directory doesn't exist (%v)", scriptsDir)
58 s.Log.Infof("SDK scripts dir: %s", scriptsDir)
60 dirs, err := filepath.Glob(path.Join(scriptsDir, "*"))
62 s.Log.Errorf("Error while retrieving SDK scripts: dir=%s, error=%s", scriptsDir, err.Error())
67 defer s.mutex.Unlock()
69 // Foreach directories in scripts/sdk
71 for _, d := range dirs {
76 sdksList, err := ListCrossSDK(d, s.Log)
78 // allow to use XDS even if error on list
79 s.Log.Errorf("Cannot retrieve SDK list: %v", err)
81 s.LogSillyf("'%s' SDKs list: %v", d, sdksList)
83 for _, sdk := range sdksList {
84 cSdk, err := s._createNewCrossSDK(sdk, d, false, false)
86 s.Log.Debugf("Error while processing SDK sdk=%v\n err=%s", sdk, err.Error())
90 if cSdk.sdk.Status == xsapiv1.SdkStatusInstalled {
94 s.SdksFamilies[cSdk.sdk.FamilyConf.FamilyName] = &cSdk.sdk.FamilyConf
98 ctx.Log.Debugf("Cross SDKs: %d defined, %d installed", len(s.Sdks), nbInstalled)
100 // Start monitor thread to detect new SDKs
101 sdksDirs := []string{}
102 for _, sf := range s.SdksFamilies {
103 sdksDirs = append(sdksDirs, sf.RootDir)
106 if len(s.SdksFamilies) == 0 {
107 s.Log.Warningf("No cross SDKs definition found")
108 /* TODO: used it or cleanup
110 go s.monitorSDKInstallation(sdksDirs)
117 // _createNewCrossSDK Private function to create a new Cross SDK
118 func (s *SDKs) _createNewCrossSDK(sdk xsapiv1.SDK, scriptDir string, installing bool, force bool) (*CrossSDK, error) {
120 cSdk, err := NewCrossSDK(s.Context, sdk, scriptDir)
125 // Allow to overwrite not installed SDK or when force is set
126 if _, exist := s.Sdks[cSdk.sdk.ID]; exist {
127 if !force && cSdk.sdk.Path != "" && common.Exists(cSdk.sdk.Path) {
128 return cSdk, fmt.Errorf("SDK ID %s already installed in %s", cSdk.sdk.ID, cSdk.sdk.Path)
130 if !force && cSdk.sdk.Status != xsapiv1.SdkStatusNotInstalled {
131 return cSdk, fmt.Errorf("Duplicate SDK ID %s (use force to overwrite)", cSdk.sdk.ID)
136 errMsg := "Invalid SDK definition "
137 if installing && cSdk.sdk.Path == "" {
138 return cSdk, fmt.Errorf(errMsg + "(path not set)")
140 if installing && cSdk.sdk.URL == "" {
141 return cSdk, fmt.Errorf(errMsg + "(url not set)")
145 s.Sdks[cSdk.sdk.ID] = cSdk
150 // Stop SDKs management
151 func (s *SDKs) Stop() {
155 // monitorSDKInstallation
156 /* TODO: used it or cleanup
157 import "github.com/zillode/notify"
159 func (s *SDKs) monitorSDKInstallation(watchingDirs []string) {
161 // Set up a watchpoint listening for inotify-specific events
162 c := make(chan notify.EventInfo, 1)
164 addWatcher := func(rootDir string) error {
165 s.Log.Debugf("SDK Register watcher: rootDir=%s", rootDir)
167 if err := notify.Watch(rootDir+"/...", c, notify.Create, notify.Remove); err != nil {
168 return fmt.Errorf("SDK monitor: rootDir=%v err=%v", rootDir, err)
173 // Add directory watchers
174 for _, dir := range watchingDirs {
175 if err := addWatcher(dir); err != nil {
176 s.Log.Errorln(err.Error())
180 // Wait inotify or stop events
184 s.Log.Debugln("Stop monitorSDKInstallation")
188 s.LogSillyf("monitorSDKInstallation SDKs event %v, path %v\n", ei.Event(), ei.Path())
190 // Filter out all event that doesn't match environment file
191 if !strings.Contains(ei.Path(), "environment-setup-") {
194 dir := path.Dir(ei.Path())
196 sdk, err := s.GetByPath(dir)
198 s.Log.Warningf("Cannot find SDK path to notify creation")
199 s.LogSillyf("event: %v", ei.Event())
205 sdkDef, err := GetSDKInfo(scriptDir, sdk.URL, "", "", s.Log)
207 s.Log.Warningf("Cannot get sdk info: %v", err)
210 sdk.Path = sdkDef.Path
211 sdk.Path = sdkDef.SetupFile
213 // Emit Folder state change event
214 if err := s.events.Emit(xsapiv1.EVTSDKAdd, sdk, ""); err != nil {
215 s.Log.Warningf("Cannot notify SDK install: %v", err)
218 case notify.Remove, notify.InMovedFrom:
219 // Emit Folder state change event
220 if err := s.events.Emit(xsapiv1.EVTSDKRemove, sdk, ""); err != nil {
221 s.Log.Warningf("Cannot notify SDK remove: %v", err)
229 // ResolveID Complete an SDK ID (helper for user that can use partial ID value)
230 func (s *SDKs) ResolveID(id string) (string, error) {
236 for iid := range s.Sdks {
237 if strings.HasPrefix(iid, id) {
238 match = append(match, iid)
244 } else if len(match) == 0 {
245 return id, fmt.Errorf("Unknown sdk id")
247 return id, fmt.Errorf("Multiple sdk IDs found: %v", match)
250 // Get returns an SDK from id
251 func (s *SDKs) Get(id string) *xsapiv1.SDK {
253 defer s.mutex.Unlock()
255 sc, exist := s.Sdks[id]
262 // GetByPath Find a SDK from path
263 func (s *SDKs) GetByPath(path string) (*xsapiv1.SDK, error) {
265 return nil, fmt.Errorf("can't found sdk (empty path)")
267 for _, ss := range s.Sdks {
268 if ss.sdk.Path == path {
272 return nil, fmt.Errorf("not found")
275 // GetAll returns all existing SDKs
276 func (s *SDKs) GetAll() []xsapiv1.SDK {
278 defer s.mutex.Unlock()
279 res := []xsapiv1.SDK{}
280 for _, v := range s.Sdks {
281 res = append(res, *(*v).Get())
286 // GetEnvCmd returns the command used to initialized the environment for an SDK
287 func (s *SDKs) GetEnvCmd(id string, defaultID string) []string {
288 if id == "" && defaultID == "" {
294 defer s.mutex.Unlock()
296 if iid, err := s.ResolveID(id); err == nil {
297 if sdk, exist := s.Sdks[iid]; exist {
298 return sdk.GetEnvCmd()
302 if sdk, exist := s.Sdks[defaultID]; defaultID != "" && exist {
303 return sdk.GetEnvCmd()
306 // Return default env that may be empty
310 // Install Used to install a new SDK
311 func (s *SDKs) Install(id, filepath string, force bool, timeout int, args []string, sess *ClientSession) (*xsapiv1.SDK, error) {
318 if id != "" && filepath != "" {
319 return nil, fmt.Errorf("invalid parameter, both id and filepath are set")
323 defer s.mutex.Unlock()
326 curSdk, exist := s.Sdks[id]
328 return nil, fmt.Errorf("unknown id")
332 scriptDir = sdk.FamilyConf.ScriptsDir
334 // Update path when not set
336 sdkDef, err := GetSDKInfo(scriptDir, sdk.URL, "", "", s.Log)
337 if err != nil || sdkDef.Path == "" {
338 return nil, fmt.Errorf("cannot retrieve sdk path %v", err)
340 sdk.Path = sdkDef.Path
343 } else if filepath != "" {
344 // FIXME support any location and also sharing either by pathmap or Syncthing
345 baseDir := "${HOME}/xds-workspace/sdks"
346 sdkFilename, _ = common.ResolveEnvVar(path.Join(baseDir, path.Base(filepath)))
347 if !common.Exists(sdkFilename) {
348 return nil, fmt.Errorf("SDK file not accessible, must be in %s", baseDir)
351 for _, sf := range s.SdksFamilies {
352 sdkDef, err := GetSDKInfo(sf.ScriptsDir, "", sdkFilename, "", s.Log)
356 scriptDir = sf.ScriptsDir
360 s.Log.Debugf("GetSDKInfo error: family=%s, sdkFilename=%s, err=%v", sf.FamilyName, path.Base(sdkFilename), err)
363 return nil, fmt.Errorf("Cannot identify SDK family for %s", path.Base(filepath))
367 return nil, fmt.Errorf("invalid parameter, id or filepath must be set")
370 cSdk, err := s._createNewCrossSDK(*sdk, scriptDir, true, force)
375 // Launch script to install
376 // (note that add event will be generated by monitoring thread)
377 if err := cSdk.Install(sdkFilename, force, timeout, args, sess); err != nil {
378 return &cSdk.sdk, err
381 return &cSdk.sdk, nil
384 // AbortInstall Used to abort SDK installation
385 func (s *SDKs) AbortInstall(id string, timeout int) (*xsapiv1.SDK, error) {
388 return nil, fmt.Errorf("invalid parameter")
390 cSdk, exist := s.Sdks[id]
392 return nil, fmt.Errorf("unknown id")
396 defer s.mutex.Unlock()
398 err := cSdk.AbortInstallRemove(timeout)
400 return &cSdk.sdk, err
403 // Remove Used to uninstall a SDK
404 func (s *SDKs) Remove(id string, timeout int, sess *ClientSession) (*xsapiv1.SDK, error) {
406 cSdk, exist := s.Sdks[id]
408 return nil, fmt.Errorf("unknown id")
412 defer s.mutex.Unlock()
414 // Launch script to remove/uninstall
415 // (note that remove event will be generated by monitoring thread)
416 if err := cSdk.Remove(timeout, sess); err != nil {
417 return &cSdk.sdk, err
422 // Don't delete it from s.Sdks
423 // (always keep sdk reference to allow for example re-install)