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  }