kcl-lang.io/kpm@v0.8.7-0.20240520061008-9fc4c5efc8c7/pkg/api/kpm_run.go (about) 1 package api 2 3 import ( 4 "os" 5 "path/filepath" 6 "strings" 7 8 "kcl-lang.io/kcl-go/pkg/kcl" 9 "kcl-lang.io/kpm/pkg/client" 10 "kcl-lang.io/kpm/pkg/constants" 11 "kcl-lang.io/kpm/pkg/env" 12 "kcl-lang.io/kpm/pkg/errors" 13 "kcl-lang.io/kpm/pkg/oci" 14 "kcl-lang.io/kpm/pkg/opt" 15 pkg "kcl-lang.io/kpm/pkg/package" 16 "kcl-lang.io/kpm/pkg/reporter" 17 "kcl-lang.io/kpm/pkg/runner" 18 "kcl-lang.io/kpm/pkg/utils" 19 ) 20 21 // RunTar will compile the kcl package from a kcl package tar. 22 func RunTar(tarPath string, opts *opt.CompileOptions) (string, error) { 23 // The directory after extracting the tar package is taken as the root directory of the package, 24 // and kclvm is called to compile the kcl program under the 'destDir'. 25 // e.g. 26 // if the tar path is 'xxx/xxx/xxx/test.tar', 27 // the 'xxx/xxx/xxx/test' will be taken as the root path of the kcl package to compile. 28 compileResult, compileErr := RunTarPkg(tarPath, opts) 29 30 if compileErr != nil { 31 return "", compileErr 32 } 33 return compileResult.GetRawYamlResult(), nil 34 } 35 36 // RunOci will compile the kcl package from an OCI reference. 37 func RunOci(ociRef, version string, opts *opt.CompileOptions) (string, error) { 38 compileResult, compileErr := RunOciPkg(ociRef, version, opts) 39 40 if compileErr != nil { 41 return "", compileErr 42 } 43 return compileResult.GetRawYamlResult(), nil 44 } 45 46 // RunPkg will compile current kcl package. 47 func RunPkg(opts *opt.CompileOptions) (string, error) { 48 49 compileResult, err := RunCurrentPkg(opts) 50 if err != nil { 51 return "", err 52 } 53 54 return compileResult.GetRawYamlResult(), nil 55 } 56 57 // RunPkgInPath will load the 'KclPkg' from path 'pkgPath'. 58 // And run the kcl package with entry file in 'entryFilePath' in 'vendorMode'. 59 func RunPkgInPath(opts *opt.CompileOptions) (string, error) { 60 // Call the kcl compiler. 61 compileResult, err := RunPkgWithOpt(opts) 62 if err != nil { 63 return "", err 64 } 65 66 return compileResult.GetRawYamlResult(), nil 67 } 68 69 // CompileWithOpt will compile the kcl program without kcl package. 70 // Deprecated: This method will not be maintained in the future. Use RunWithOpts instead. 71 func RunWithOpt(opts *opt.CompileOptions) (*kcl.KCLResultList, error) { 72 // The entries will override the entries in the settings file. 73 if opts.HasSettingsYaml() && len(opts.KFilenameList) > 0 && len(opts.Entries()) > 0 { 74 opts.KFilenameList = []string{} 75 } 76 if len(opts.Entries()) > 0 { 77 for _, entry := range opts.Entries() { 78 if filepath.IsAbs(entry) { 79 opts.Merge(kcl.WithKFilenames(entry)) 80 } else { 81 opts.Merge(kcl.WithKFilenames(filepath.Join(opts.PkgPath(), entry))) 82 } 83 } 84 } else if !opts.HasSettingsYaml() && len(opts.KFilenameList) == 0 { 85 // If no entry, no kcl files and no settings files. 86 opts.Merge(kcl.WithKFilenames(opts.PkgPath())) 87 } 88 opts.Merge(kcl.WithWorkDir(opts.PkgPath())) 89 return kcl.RunWithOpts(*opts.Option) 90 } 91 92 // RunWithOpts will compile the kcl package with the compile options. 93 func RunWithOpts(opts ...opt.Option) (*kcl.KCLResultList, error) { 94 mergedOpts := opt.DefaultCompileOptions() 95 for _, opt := range opts { 96 opt(mergedOpts) 97 } 98 return runPkgWithOpt(mergedOpts) 99 } 100 101 // getAbsInputPath will return the abs path of the file path described by '--input'. 102 // If the path exists after 'inputPath' is computed as a full path, it will be returned. 103 // If not, the kpm checks whether the full path of 'pkgPath/inputPath' exists, 104 // If the full path of 'pkgPath/inputPath' exists, it will be returned. 105 // If not, getAbsInputPath returns 'entry file not found' error. 106 func getAbsInputPath(pkgPath string, inputPath string) (string, error) { 107 absPath, err := filepath.Abs(filepath.Join(pkgPath, inputPath)) 108 if err != nil { 109 return "", err 110 } 111 112 if utils.DirExists(absPath) { 113 return absPath, nil 114 } 115 116 return "", errors.EntryFileNotFound 117 } 118 119 // RunPkgWithOpt will compile the kcl package with the compile options. 120 // Deprecated: This method will not be maintained in the future. Use RunWithOpts instead. 121 func RunPkgWithOpt(opts *opt.CompileOptions) (*kcl.KCLResultList, error) { 122 kpmcli, err := client.NewKpmClient() 123 kpmcli.SetNoSumCheck(opts.NoSumCheck()) 124 if err != nil { 125 return nil, err 126 } 127 return run(kpmcli, opts) 128 } 129 130 func runPkgWithOpt(opts *opt.CompileOptions) (*kcl.KCLResultList, error) { 131 kpmcli, err := client.NewKpmClient() 132 kpmcli.SetNoSumCheck(opts.NoSumCheck()) 133 if err != nil { 134 return nil, err 135 } 136 return run(kpmcli, opts) 137 } 138 139 // RunCurrentPkg will compile the current kcl package. 140 func RunCurrentPkg(opts *opt.CompileOptions) (*kcl.KCLResultList, error) { 141 pwd, err := os.Getwd() 142 opts.SetPkgPath(pwd) 143 144 if err != nil { 145 return nil, reporter.NewErrorEvent(reporter.Bug, err, "internal bugs, failed to load working directory.") 146 } 147 148 return RunPkgWithOpt(opts) 149 } 150 151 // RunTarPkg will compile the kcl package from a kcl package tar. 152 func RunTarPkg(tarPath string, opts *opt.CompileOptions) (*kcl.KCLResultList, error) { 153 absTarPath, err := utils.AbsTarPath(tarPath) 154 if err != nil { 155 return nil, err 156 } 157 // Extract the tar package to a directory with the same name. 158 // e.g. 159 // 'xxx/xxx/xxx/test.tar' will be extracted to the directory 'xxx/xxx/xxx/test'. 160 destDir := strings.TrimSuffix(absTarPath, filepath.Ext(absTarPath)) 161 err = utils.UnTarDir(absTarPath, destDir) 162 if err != nil { 163 return nil, err 164 } 165 166 opts.SetPkgPath(destDir) 167 kpmcli, err := client.NewKpmClient() 168 kpmcli.SetNoSumCheck(opts.NoSumCheck()) 169 if err != nil { 170 return nil, err 171 } 172 // The directory after extracting the tar package is taken as the root directory of the package, 173 // and kclvm is called to compile the kcl program under the 'destDir'. 174 // e.g. 175 // if the tar path is 'xxx/xxx/xxx/test.tar', 176 // the 'xxx/xxx/xxx/test' will be taken as the root path of the kcl package to compile. 177 return run(kpmcli, opts) 178 } 179 180 // RunOciPkg will compile the kcl package from an OCI reference. 181 func RunOciPkg(ociRef, version string, opts *opt.CompileOptions) (*kcl.KCLResultList, error) { 182 kpmcli, err := client.NewKpmClient() 183 if err != nil { 184 return nil, err 185 } 186 kpmcli.SetNoSumCheck(opts.NoSumCheck()) 187 ociOpts, err := kpmcli.ParseOciOptionFromString(ociRef, version) 188 189 if err != nil { 190 return nil, err 191 } 192 193 // 1. Create the temporary directory to pull the tar. 194 tmpDir, err := os.MkdirTemp("", "") 195 if err != nil { 196 return nil, reporter.NewErrorEvent(reporter.Bug, err, "internal bugs, please contact us to fix it.") 197 } 198 // clean the temp dir. 199 defer os.RemoveAll(tmpDir) 200 201 localPath := ociOpts.SanitizePathWithSuffix(tmpDir) 202 203 // 2. Pull the tar. 204 err = oci.Pull(localPath, ociOpts.Reg, ociOpts.Repo, ociOpts.Tag, kpmcli.GetSettings()) 205 206 if err != (*reporter.KpmEvent)(nil) { 207 return nil, err 208 } 209 210 // 3.Get the (*.tar) file path. 211 matches, err := filepath.Glob(filepath.Join(localPath, constants.KCL_PKG_TAR)) 212 if err != nil || len(matches) != 1 { 213 if err != nil { 214 return nil, reporter.NewErrorEvent(reporter.FailedGetPkg, err, "failed to pull kcl package") 215 } else { 216 return nil, errors.FailedPull 217 } 218 } 219 220 // 4. Untar the tar file. 221 absTarPath, err := utils.AbsTarPath(matches[0]) 222 if err != nil { 223 return nil, err 224 } 225 // Extract the tar package to a directory with the same name. 226 // e.g. 227 // 'xxx/xxx/xxx/test.tar' will be extracted to the directory 'xxx/xxx/xxx/test'. 228 destDir := strings.TrimSuffix(absTarPath, filepath.Ext(absTarPath)) 229 err = utils.UnTarDir(absTarPath, destDir) 230 if err != nil { 231 return nil, err 232 } 233 234 opts.SetPkgPath(destDir) 235 return run(kpmcli, opts) 236 } 237 238 // 'run' will compile the kcl package from the compile options by kpm client. 239 func run(kpmcli *client.KpmClient, opts *opt.CompileOptions) (*kcl.KCLResultList, error) { 240 pkgPath, err := filepath.Abs(opts.PkgPath()) 241 if err != nil { 242 return nil, reporter.NewErrorEvent(reporter.Bug, err, "internal bugs, please contact us to fix it.") 243 } 244 245 kclPkg, err := pkg.LoadKclPkg(pkgPath) 246 if err != nil { 247 return nil, err 248 } 249 250 kclPkg.SetVendorMode(opts.IsVendor()) 251 252 globalPkgPath, err := env.GetAbsPkgPath() 253 if err != nil { 254 return nil, err 255 } 256 257 err = kclPkg.ValidateKpmHome(globalPkgPath) 258 if err != (*reporter.KpmEvent)(nil) { 259 return nil, err 260 } 261 262 if len(opts.Entries()) > 0 { 263 // add entry from '--input' 264 for _, entry := range opts.Entries() { 265 if filepath.IsAbs(entry) { 266 opts.Merge(kcl.WithKFilenames(entry)) 267 } else { 268 opts.Merge(kcl.WithKFilenames(filepath.Join(opts.PkgPath(), entry))) 269 } 270 } 271 // add entry from 'kcl.mod' 272 } else if kclPkg.HasProfile() { 273 opts.Merge(*kclPkg.GetKclOpts()) 274 } else if !opts.HasSettingsYaml() { 275 // no entry 276 opts.Merge(kcl.WithKFilenames(opts.PkgPath())) 277 } 278 opts.Merge(kcl.WithWorkDir(opts.PkgPath())) 279 280 // Calculate the absolute path of entry file described by '--input'. 281 compiler := runner.NewCompilerWithOpts(opts) 282 283 kpmcli.SetLogWriter(opts.LogWriter()) 284 285 // Call the kcl compiler. 286 compileResult, err := kpmcli.Compile(kclPkg, compiler) 287 288 if err != nil { 289 return nil, reporter.NewErrorEvent(reporter.CompileFailed, err, "failed to compile the kcl package") 290 } 291 292 return compileResult, nil 293 }