github.com/dahs81/otto@v0.2.1-0.20160126165905-6400716cf085/appfile/load/loader.go (about) 1 package load 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 9 "github.com/hashicorp/otto/app" 10 "github.com/hashicorp/otto/appfile" 11 "github.com/hashicorp/otto/appfile/detect" 12 "github.com/hashicorp/otto/otto" 13 ) 14 15 // Loader is used to load an Appfile. 16 // 17 // This logic used to live in the "compile" command directly but was 18 // extracted so it could be tested in isolation better and in case there 19 // is any interest in reusability. 20 // 21 // The purpose of the loader is to do the multi-step Appfile load outlined 22 // below. As input, we have the "real" Appfile, which is the real physical 23 // Appfile if it exists (or nil if it doesn't). 24 // 25 // 1.) Detect the type, known as the "detected" Appfile 26 // 27 // 2.) Merge with the detected Appfile with the real Appfile. If 28 // the "detect" setting is set to "false", then we're done. Otherwise, 29 // continue. 30 // 31 // 3.) Instantiate the proper plugin for the app type and call the Implicit 32 // API to get an Appfile known as the "implicit" Appfile. 33 // 34 // 4.) Merge in the order: detected, implicit, real. Return the real 35 // Appfile. 36 // 37 type Loader struct { 38 // Detector is the detector configuration. If this is nil then 39 // no type detection will be done. 40 Detector *detect.Config 41 42 // Compiler is the appfile compiler that we're using. This is used 43 // to do a minimal compile (MinCompile) to realize imports of 44 // Appfiles prior to implicit loading. 45 Compiler *appfile.Compiler 46 47 // Apps will be used to load the proper app implementation for 48 // implicit loading. 49 Apps map[app.Tuple]app.Factory 50 } 51 52 func (l *Loader) Load(f *appfile.File, dir string) (*appfile.File, error) { 53 realFile := f 54 55 // Load the "detected" Appfile 56 appDef, err := appfile.Default(dir, l.Detector) 57 if err != nil { 58 return nil, fmt.Errorf("Error detecting app type: %s", err) 59 } 60 61 // Merge the detected Appfile with the real Appfile 62 var merged appfile.File 63 if err := merged.Merge(appDef); err != nil { 64 return nil, fmt.Errorf("Error loading Appfile: %s", err) 65 } 66 if realFile != nil { 67 if err := merged.Merge(realFile); err != nil { 68 return nil, fmt.Errorf("Error loading Appfile: %s", err) 69 } 70 } 71 realFile = &merged 72 73 // If we have no application type, there is nothing more to do 74 if realFile == nil || realFile.Application.Type == "" { 75 return realFile, nil 76 } 77 78 // If we're not configured to do any further detection, return it 79 if !realFile.Application.Detect { 80 return realFile, nil 81 } 82 83 // Minimally compile the file that we can use to create a core 84 compiled, err := l.Compiler.MinCompile(realFile) 85 if err != nil { 86 return nil, err 87 } 88 89 // Create a temporary directory we use for the core 90 td, err := ioutil.TempDir("", "otto") 91 if err != nil { 92 return nil, err 93 } 94 defer os.RemoveAll(td) 95 96 // Create a core 97 core, err := otto.NewCore(&otto.CoreConfig{ 98 DataDir: filepath.Join(td, "data"), 99 LocalDir: filepath.Join(td, "local"), 100 CompileDir: filepath.Join(td, "compile"), 101 Appfile: compiled, 102 Apps: l.Apps, 103 }) 104 if err != nil { 105 return nil, err 106 } 107 108 // Get the app implementation 109 appImpl, appCtx, err := core.App() 110 if err != nil { 111 return nil, err 112 } 113 defer app.Close(appImpl) 114 115 // Load the implicit Appfile 116 implicit, err := appImpl.Implicit(appCtx) 117 if err != nil { 118 return nil, err 119 } 120 121 var final appfile.File 122 if err := final.Merge(appDef); err != nil { 123 return nil, fmt.Errorf("Error loading Appfile: %s", err) 124 } 125 if implicit != nil { 126 if err := final.Merge(implicit); err != nil { 127 return nil, fmt.Errorf("Error loading Appfile: %s", err) 128 } 129 } 130 if f != nil { 131 if err := final.Merge(f); err != nil { 132 return nil, fmt.Errorf("Error loading Appfile: %s", err) 133 } 134 } 135 136 return &final, nil 137 }