github.com/TheSpiritXIII/controller-tools@v0.14.1/pkg/genall/output.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package genall 18 19 import ( 20 "fmt" 21 "io" 22 "os" 23 "path/filepath" 24 25 "github.com/TheSpiritXIII/controller-tools/pkg/loader" 26 ) 27 28 // nopCloser is a WriteCloser whose Close 29 // is a no-op. 30 type nopCloser struct { 31 io.Writer 32 } 33 34 func (n nopCloser) Close() error { 35 return nil 36 } 37 38 // DirectoryPerGenerator produces output rules mapping output to a different subdirectory 39 // of the given base directory for each generator (with each subdirectory specified as 40 // the key in the input map). 41 func DirectoryPerGenerator(base string, generators map[string]*Generator) OutputRules { 42 rules := OutputRules{ 43 Default: OutputArtifacts{Config: OutputToDirectory(base)}, 44 ByGenerator: make(map[*Generator]OutputRule, len(generators)), 45 } 46 47 for name, gen := range generators { 48 rules.ByGenerator[gen] = OutputArtifacts{ 49 Config: OutputToDirectory(filepath.Join(base, name)), 50 } 51 } 52 53 return rules 54 } 55 56 // OutputRules defines how to output artificats on a per-generator basis. 57 type OutputRules struct { 58 // Default is the output rule used when no specific per-generator overrides match. 59 Default OutputRule 60 // ByGenerator contains specific per-generator overrides. 61 // NB(directxman12): this is a pointer to avoid issues if a given Generator becomes unhashable 62 // (interface values compare by "dereferencing" their internal pointer first, whereas pointers 63 // compare by the actual pointer itself). 64 ByGenerator map[*Generator]OutputRule 65 } 66 67 // ForGenerator returns the output rule that should be used 68 // by the given Generator. 69 func (o OutputRules) ForGenerator(gen *Generator) OutputRule { 70 if forGen, specific := o.ByGenerator[gen]; specific { 71 return forGen 72 } 73 return o.Default 74 } 75 76 // OutputRule defines how to output artifacts from a generator. 77 type OutputRule interface { 78 // Open opens the given artifact path for writing. If a package is passed, 79 // the artifact is considered to be used as part of the package (e.g. 80 // generated code), while a nil package indicates that the artifact is 81 // config (or something else not involved in Go compilation). 82 Open(pkg *loader.Package, path string) (io.WriteCloser, error) 83 } 84 85 // OutputToNothing skips outputting anything. 86 var OutputToNothing = outputToNothing{} 87 88 // +controllertools:marker:generateHelp:category="" 89 90 // outputToNothing skips outputting anything. 91 type outputToNothing struct{} 92 93 func (o outputToNothing) Open(_ *loader.Package, _ string) (io.WriteCloser, error) { 94 return nopCloser{io.Discard}, nil 95 } 96 97 // +controllertools:marker:generateHelp:category="" 98 99 // OutputToDirectory outputs each artifact to the given directory, regardless 100 // of if it's package-associated or not. 101 type OutputToDirectory string 102 103 func (o OutputToDirectory) Open(_ *loader.Package, itemPath string) (io.WriteCloser, error) { 104 // ensure the directory exists 105 if err := os.MkdirAll(filepath.Dir(filepath.Join(string(o), itemPath)), os.ModePerm); err != nil { 106 return nil, err 107 } 108 path := filepath.Join(string(o), itemPath) 109 return os.Create(path) 110 } 111 112 // OutputToStdout outputs everything to standard-out, with no separation. 113 // 114 // Generally useful for single-artifact outputs. 115 var OutputToStdout = outputToStdout{} 116 117 // +controllertools:marker:generateHelp:category="" 118 119 // outputToStdout outputs everything to standard-out, with no separation. 120 // 121 // Generally useful for single-artifact outputs. 122 type outputToStdout struct{} 123 124 func (o outputToStdout) Open(_ *loader.Package, _ string) (io.WriteCloser, error) { 125 return nopCloser{os.Stdout}, nil 126 } 127 128 // +controllertools:marker:generateHelp:category="" 129 130 // OutputArtifacts outputs artifacts to different locations, depending on 131 // whether they're package-associated or not. 132 // 133 // Non-package associated artifacts 134 // are output to the Config directory, while package-associated ones are output 135 // to their package's source files' directory, unless an alternate path is 136 // specified in Code. 137 type OutputArtifacts struct { 138 // Config points to the directory to which to write configuration. 139 Config OutputToDirectory 140 // Code overrides the directory in which to write new code (defaults to where the existing code lives). 141 Code OutputToDirectory `marker:",optional"` 142 } 143 144 func (o OutputArtifacts) Open(pkg *loader.Package, itemPath string) (io.WriteCloser, error) { 145 if pkg == nil { 146 return o.Config.Open(pkg, itemPath) 147 } 148 149 if o.Code != "" { 150 return o.Code.Open(pkg, itemPath) 151 } 152 153 if len(pkg.CompiledGoFiles) == 0 { 154 return nil, fmt.Errorf("cannot output to a package with no path on disk") 155 } 156 outDir := filepath.Dir(pkg.CompiledGoFiles[0]) 157 outPath := filepath.Join(outDir, itemPath) 158 return os.Create(outPath) 159 }