github.com/alex123012/deckhouse-controller-tools@v0.0.0-20230510090815-d594daf1af8c/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 "io/ioutil" 23 "os" 24 "path/filepath" 25 26 "sigs.k8s.io/controller-tools/pkg/loader" 27 ) 28 29 // nopCloser is a WriteCloser whose Close 30 // is a no-op. 31 type nopCloser struct { 32 io.Writer 33 } 34 35 func (n nopCloser) Close() error { 36 return nil 37 } 38 39 // DirectoryPerGenerator produces output rules mapping output to a different subdirectory 40 // of the given base directory for each generator (with each subdirectory specified as 41 // the key in the input map). 42 func DirectoryPerGenerator(base string, generators map[string]*Generator) OutputRules { 43 rules := OutputRules{ 44 Default: OutputArtifacts{Config: OutputToDirectory(base)}, 45 ByGenerator: make(map[*Generator]OutputRule, len(generators)), 46 } 47 48 for name, gen := range generators { 49 rules.ByGenerator[gen] = OutputArtifacts{ 50 Config: OutputToDirectory(filepath.Join(base, name)), 51 } 52 } 53 54 return rules 55 } 56 57 // OutputRules defines how to output artificats on a per-generator basis. 58 type OutputRules struct { 59 // Default is the output rule used when no specific per-generator overrides match. 60 Default OutputRule 61 // ByGenerator contains specific per-generator overrides. 62 // NB(directxman12): this is a pointer to avoid issues if a given Generator becomes unhashable 63 // (interface values compare by "dereferencing" their internal pointer first, whereas pointers 64 // compare by the actual pointer itself). 65 ByGenerator map[*Generator]OutputRule 66 } 67 68 // ForGenerator returns the output rule that should be used 69 // by the given Generator. 70 func (o OutputRules) ForGenerator(gen *Generator) OutputRule { 71 if forGen, specific := o.ByGenerator[gen]; specific { 72 return forGen 73 } 74 return o.Default 75 } 76 77 // OutputRule defines how to output artifacts from a generator. 78 type OutputRule interface { 79 // Open opens the given artifact path for writing. If a package is passed, 80 // the artifact is considered to be used as part of the package (e.g. 81 // generated code), while a nil package indicates that the artifact is 82 // config (or something else not involved in Go compilation). 83 Open(pkg *loader.Package, path string) (io.WriteCloser, error) 84 } 85 86 // OutputToNothing skips outputting anything. 87 var OutputToNothing = outputToNothing{} 88 89 // +controllertools:marker:generateHelp:category="" 90 91 // outputToNothing skips outputting anything. 92 type outputToNothing struct{} 93 94 func (o outputToNothing) Open(_ *loader.Package, _ string) (io.WriteCloser, error) { 95 return nopCloser{ioutil.Discard}, nil 96 } 97 98 // +controllertools:marker:generateHelp:category="" 99 100 // OutputToDirectory outputs each artifact to the given directory, regardless 101 // of if it's package-associated or not. 102 type OutputToDirectory string 103 104 func (o OutputToDirectory) Open(_ *loader.Package, itemPath string) (io.WriteCloser, error) { 105 // ensure the directory exists 106 if err := os.MkdirAll(filepath.Dir(filepath.Join(string(o), itemPath)), os.ModePerm); err != nil { 107 return nil, err 108 } 109 path := filepath.Join(string(o), itemPath) 110 return os.Create(path) 111 } 112 113 // OutputToStdout outputs everything to standard-out, with no separation. 114 // 115 // Generally useful for single-artifact outputs. 116 var OutputToStdout = outputToStdout{} 117 118 // +controllertools:marker:generateHelp:category="" 119 120 // outputToStdout outputs everything to standard-out, with no separation. 121 // 122 // Generally useful for single-artifact outputs. 123 type outputToStdout struct{} 124 125 func (o outputToStdout) Open(_ *loader.Package, _ string) (io.WriteCloser, error) { 126 return nopCloser{os.Stdout}, nil 127 } 128 129 // +controllertools:marker:generateHelp:category="" 130 131 // OutputArtifacts outputs artifacts to different locations, depending on 132 // whether they're package-associated or not. 133 // 134 // Non-package associated artifacts 135 // are output to the Config directory, while package-associated ones are output 136 // to their package's source files' directory, unless an alternate path is 137 // specified in Code. 138 type OutputArtifacts struct { 139 // Config points to the directory to which to write configuration. 140 Config OutputToDirectory 141 // Code overrides the directory in which to write new code (defaults to where the existing code lives). 142 Code OutputToDirectory `marker:",optional"` 143 } 144 145 func (o OutputArtifacts) Open(pkg *loader.Package, itemPath string) (io.WriteCloser, error) { 146 if pkg == nil { 147 return o.Config.Open(pkg, itemPath) 148 } 149 150 if o.Code != "" { 151 return o.Code.Open(pkg, itemPath) 152 } 153 154 if len(pkg.CompiledGoFiles) == 0 { 155 return nil, fmt.Errorf("cannot output to a package with no path on disk") 156 } 157 outDir := filepath.Dir(pkg.CompiledGoFiles[0]) 158 outPath := filepath.Join(outDir, itemPath) 159 return os.Create(outPath) 160 }