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())
66 // Update SDK DB on startup by default (can be disable using config file)
68 if s.Config.FileConf.SdkDbUpdate != "startup" {
73 defer s.mutex.Unlock()
75 // Foreach directories in scripts/sdk
77 for _, d := range dirs {
82 sdksList, err := ListCrossSDK(d, update, s.Log)
84 // allow to use XDS even if error on list
85 s.Log.Errorf("Cannot retrieve SDK list: %v", err)
86 sdksList, _ = ListCrossSDK(d, false, s.Log)
88 s.LogSillyf("'%s' SDKs list: %v", d, sdksList)
90 for _, sdk := range sdksList {
91 cSdk, err := s._createNewCrossSDK(sdk, d, false, false)
93 s.Log.Debugf("Error while processing SDK sdk=%v\n err=%s", sdk, err.Error())
97 if cSdk.sdk.Status == xsapiv1.SdkStatusInstalled {
101 s.SdksFamilies[cSdk.sdk.FamilyConf.FamilyName] = &cSdk.sdk.FamilyConf
105 ctx.Log.Debugf("Cross SDKs: %d defined, %d installed", len(s.Sdks), nbInstalled)
107 // Start monitor thread to detect new SDKs
108 sdksDirs := []string{}
109 for _, sf := range s.SdksFamilies {
110 sdksDirs = append(sdksDirs, sf.RootDir)
113 if len(s.SdksFamilies) == 0 {
114 s.Log.Warningf("No cross SDKs definition found")
115 /* TODO: used it or cleanup
117 go s.monitorSDKInstallation(sdksDirs)
124 // _createNewCrossSDK Private function to create a new Cross SDK
125 func (s *SDKs) _createNewCrossSDK(sdk xsapiv1.SDK, scriptDir string, installing bool, force bool) (*CrossSDK, error) {
127 cSdk, err := NewCrossSDK(s.Context, sdk, scriptDir)
132 // Allow to overwrite not installed SDK or when force is set
133 if _, exist := s.Sdks[cSdk.sdk.ID]; exist {
134 if !force && cSdk.sdk.Path != "" && common.Exists(cSdk.sdk.Path) {
135 return cSdk, fmt.Errorf("SDK ID %s already installed in %s", cSdk.sdk.ID, cSdk.sdk.Path)
137 if !force && cSdk.sdk.Status != xsapiv1.SdkStatusNotInstalled {
138 return cSdk, fmt.Errorf("Duplicate SDK ID %s (use force to overwrite)", cSdk.sdk.ID)
143 errMsg := "Invalid SDK definition "
144 if installing && cSdk.sdk.Path == "" {
145 return cSdk, fmt.Errorf(errMsg + "(path not set)")
147 if installing && cSdk.sdk.URL == "" {
148 return cSdk, fmt.Errorf(errMsg + "(url not set)")
152 s.Sdks[cSdk.sdk.ID] = cSdk
157 // Stop SDKs management
158 func (s *SDKs) Stop() {
162 // monitorSDKInstallation
163 /* TODO: used it or cleanup
164 import "github.com/zillode/notify"
166 func (s *SDKs) monitorSDKInstallation(watchingDirs []string) {
168 // Set up a watchpoint listening for inotify-specific events
169 c := make(chan notify.EventInfo, 1)
171 addWatcher := func(rootDir string) error {
172 s.Log.Debugf("SDK Register watcher: rootDir=%s", rootDir)
174 if err := notify.Watch(rootDir+"/...", c, notify.Create, notify.Remove); err != nil {
175 return fmt.Errorf("SDK monitor: rootDir=%v err=%v", rootDir, err)
180 // Add directory watchers
181 for _, dir := range watchingDirs {
182 if err := addWatcher(dir); err != nil {
183 s.Log.Errorln(err.Error())
187 // Wait inotify or stop events
191 s.Log.Debugln("Stop monitorSDKInstallation")
195 s.LogSillyf("monitorSDKInstallation SDKs event %v, path %v\n", ei.Event(), ei.Path())
197 // Filter out all event that doesn't match environment file
198 if !strings.Contains(ei.Path(), "environment-setup-") {
201 dir := path.Dir(ei.Path())
203 sdk, err := s.GetByPath(dir)
205 s.Log.Warningf("Cannot find SDK path to notify creation")
206 s.LogSillyf("event: %v", ei.Event())
212 sdkDef, err := GetSDKInfo(scriptDir, sdk.URL, "", "", s.Log)
214 s.Log.Warningf("Cannot get sdk info: %v", err)
217 sdk.Path = sdkDef.Path
218 sdk.Path = sdkDef.SetupFile
220 // Emit Folder state change event
221 if err := s.events.Emit(xsapiv1.EVTSDKAdd, sdk, ""); err != nil {
222 s.Log.Warningf("Cannot notify SDK install: %v", err)
225 case notify.Remove, notify.InMovedFrom:
226 // Emit Folder state change event
227 if err := s.events.Emit(xsapiv1.EVTSDKRemove, sdk, ""); err != nil {
228 s.Log.Warningf("Cannot notify SDK remove: %v", err)
236 // ResolveID Complete an SDK ID (helper for user that can use partial ID value)
237 func (s *SDKs) ResolveID(id string) (string, error) {
243 for iid := range s.Sdks {
244 if strings.HasPrefix(iid, id) {
245 match = append(match, iid)
251 } else if len(match) == 0 {
252 return id, fmt.Errorf("Unknown sdk id")
254 return id, fmt.Errorf("Multiple sdk IDs found: %v", match)
257 // Get returns an SDK from id
258 func (s *SDKs) Get(id string) *xsapiv1.SDK {
260 defer s.mutex.Unlock()
262 sc, exist := s.Sdks[id]
269 // GetByPath Find a SDK from path
270 func (s *SDKs) GetByPath(path string) (*xsapiv1.SDK, error) {
272 return nil, fmt.Errorf("can't found sdk (empty path)")
274 for _, ss := range s.Sdks {
275 if ss.sdk.Path == path {
279 return nil, fmt.Errorf("not found")
282 // GetAll returns all existing SDKs
283 func (s *SDKs) GetAll() []xsapiv1.SDK {
285 defer s.mutex.Unlock()
286 res := []xsapiv1.SDK{}
287 for _, v := range s.Sdks {
288 res = append(res, *(*v).Get())
293 // GetEnvCmd returns the command used to initialized the environment for an SDK
294 func (s *SDKs) GetEnvCmd(id string, defaultID string) []string {
295 if id == "" && defaultID == "" {
301 defer s.mutex.Unlock()
303 if iid, err := s.ResolveID(id); err == nil {
304 if sdk, exist := s.Sdks[iid]; exist {
305 return sdk.GetEnvCmd()
309 if sdk, exist := s.Sdks[defaultID]; defaultID != "" && exist {
310 return sdk.GetEnvCmd()
313 // Return default env that may be empty
317 // Install Used to install a new SDK
318 func (s *SDKs) Install(id, filepath string, force bool, timeout int, args []string, sess *ClientSession) (*xsapiv1.SDK, error) {
325 if id != "" && filepath != "" {
326 return nil, fmt.Errorf("invalid parameter, both id and filepath are set")
330 defer s.mutex.Unlock()
333 curSdk, exist := s.Sdks[id]
335 return nil, fmt.Errorf("unknown id")
339 scriptDir = sdk.FamilyConf.ScriptsDir
341 // Update path when not set
343 sdkDef, err := GetSDKInfo(scriptDir, sdk.URL, "", "", s.Log)
344 if err != nil || sdkDef.Path == "" {
345 return nil, fmt.Errorf("cannot retrieve sdk path %v", err)
347 sdk.Path = sdkDef.Path
350 } else if filepath != "" {
351 // FIXME support any location and also sharing either by pathmap or Syncthing
352 baseDir := "${HOME}/xds-workspace/sdks"
353 sdkFilename, _ = common.ResolveEnvVar(path.Join(baseDir, path.Base(filepath)))
354 if !common.Exists(sdkFilename) {
355 return nil, fmt.Errorf("SDK file not accessible, must be in %s", baseDir)
358 for _, sf := range s.SdksFamilies {
359 sdkDef, err := GetSDKInfo(sf.ScriptsDir, "", sdkFilename, "", s.Log)
363 scriptDir = sf.ScriptsDir
367 s.Log.Debugf("GetSDKInfo error: family=%s, sdkFilename=%s, err=%v", sf.FamilyName, path.Base(sdkFilename), err)
370 return nil, fmt.Errorf("Cannot identify SDK family for %s", path.Base(filepath))
374 return nil, fmt.Errorf("invalid parameter, id or filepath must be set")
377 cSdk, err := s._createNewCrossSDK(*sdk, scriptDir, true, force)
382 // Launch script to install
383 // (note that add event will be generated by monitoring thread)
384 if err := cSdk.Install(sdkFilename, force, timeout, args, sess); err != nil {
385 return &cSdk.sdk, err
388 return &cSdk.sdk, nil
391 // AbortInstall Used to abort SDK installation
392 func (s *SDKs) AbortInstall(id string, timeout int) (*xsapiv1.SDK, error) {
395 return nil, fmt.Errorf("invalid parameter")
397 cSdk, exist := s.Sdks[id]
399 return nil, fmt.Errorf("unknown id")
403 defer s.mutex.Unlock()
405 err := cSdk.AbortInstallRemove(timeout)
407 return &cSdk.sdk, err
410 // Remove Used to uninstall a SDK
411 func (s *SDKs) Remove(id string, timeout int, sess *ClientSession) (*xsapiv1.SDK, error) {
413 cSdk, exist := s.Sdks[id]
415 return nil, fmt.Errorf("unknown id")
419 defer s.mutex.Unlock()
421 // Launch script to remove/uninstall
422 // (note that remove event will be generated by monitoring thread)
423 if err := cSdk.Remove(timeout, sess); err != nil {
424 return &cSdk.sdk, err
429 // Don't delete it from s.Sdks
430 // (always keep sdk reference to allow for example re-install)