Some checks are pending
Docs Deploy / build_and_deploy (push) Waiting to run
Generate Docs / cli (push) Waiting to run
Generate Config Doc / cli (push) Waiting to run
Go formatting / go-formatting (push) Waiting to run
Check links / markdown-link-check (push) Waiting to run
Integration / pre-test (push) Waiting to run
Integration / test on (push) Blocked by required conditions
Integration / status (push) Blocked by required conditions
Lint / Lint Go code (push) Waiting to run
Test / test (ubuntu-latest) (push) Waiting to run
280 lines
7.3 KiB
Go
280 lines
7.3 KiB
Go
package chain
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/ignite/cli/v29/ignite/pkg/archive"
|
|
"github.com/ignite/cli/v29/ignite/pkg/cache"
|
|
"github.com/ignite/cli/v29/ignite/pkg/checksum"
|
|
"github.com/ignite/cli/v29/ignite/pkg/cmdrunner"
|
|
"github.com/ignite/cli/v29/ignite/pkg/cmdrunner/exec"
|
|
"github.com/ignite/cli/v29/ignite/pkg/cmdrunner/step"
|
|
"github.com/ignite/cli/v29/ignite/pkg/dirchange"
|
|
"github.com/ignite/cli/v29/ignite/pkg/errors"
|
|
"github.com/ignite/cli/v29/ignite/pkg/events"
|
|
"github.com/ignite/cli/v29/ignite/pkg/goanalysis"
|
|
"github.com/ignite/cli/v29/ignite/pkg/gocmd"
|
|
"github.com/ignite/cli/v29/ignite/pkg/xstrings"
|
|
)
|
|
|
|
const (
|
|
releaseDir = "release"
|
|
releaseChecksumKey = "release_checksum"
|
|
modChecksumKey = "go_mod_checksum"
|
|
buildDirchangeCacheNamespace = "build.dirchange"
|
|
consumerDevel = "consumer_devel"
|
|
)
|
|
|
|
// Build builds and installs app binaries.
|
|
func (c *Chain) Build(
|
|
ctx context.Context,
|
|
cacheStorage cache.Storage,
|
|
buildTags []string,
|
|
output string,
|
|
skipProto, debug bool,
|
|
) (binaryName string, err error) {
|
|
if err := c.setup(); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if err := c.build(ctx, cacheStorage, buildTags, output, skipProto, false, debug); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return c.Binary()
|
|
}
|
|
|
|
func (c *Chain) build(
|
|
ctx context.Context,
|
|
cacheStorage cache.Storage,
|
|
buildTags []string,
|
|
output string,
|
|
skipProto, generateClients, debug bool,
|
|
) (err error) {
|
|
defer func() {
|
|
var exitErr *exec.ExitError
|
|
|
|
if errors.As(err, &exitErr) || errors.Is(err, goanalysis.ErrMultipleMainPackagesFound) {
|
|
err = &CannotBuildAppError{err}
|
|
}
|
|
}()
|
|
|
|
if !skipProto {
|
|
// Generate code from proto files
|
|
if err := c.generateFromConfig(ctx, cacheStorage, generateClients); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
cfg, err := c.Config()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if cfg.IsConsumerChain() {
|
|
// When building a non-release consumer chain (which is the case for this
|
|
// build() method), enable consumerDevel (see templates consumer_devel and
|
|
// consumer_final for more info).
|
|
buildTags = append(buildTags, consumerDevel)
|
|
}
|
|
|
|
buildFlags, err := c.preBuild(ctx, cacheStorage, buildTags...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if debug {
|
|
// Add flags to disable binary optimizations and inlining to allow debugging
|
|
buildFlags = append(buildFlags, gocmd.FlagGcflags, gocmd.FlagGcflagsValueDebug)
|
|
}
|
|
|
|
binary, err := c.Binary()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
path, err := c.discoverMain(c.app.Path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return gocmd.BuildPath(ctx, output, binary, path, buildFlags)
|
|
}
|
|
|
|
// BuildRelease builds binaries for a release. targets is a list
|
|
// of GOOS:GOARCH when provided. It defaults to your system when no targets provided.
|
|
// prefix is used as prefix to tarballs containing each target.
|
|
func (c *Chain) BuildRelease(
|
|
ctx context.Context,
|
|
cacheStorage cache.Storage,
|
|
buildParams []string,
|
|
output, prefix string,
|
|
targets ...string,
|
|
) (releasePath string, err error) {
|
|
if prefix == "" {
|
|
prefix = c.app.Name
|
|
}
|
|
if len(targets) == 0 {
|
|
targets = []string{gocmd.BuildTarget(runtime.GOOS, runtime.GOARCH)}
|
|
}
|
|
|
|
// prepare for build.
|
|
if err := c.setup(); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
buildFlags, err := c.preBuild(ctx, cacheStorage, buildParams...)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
binary, err := c.Binary()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
mainPath, err := c.discoverMain(c.app.Path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
releasePath = output
|
|
if releasePath == "" {
|
|
releasePath = filepath.Join(c.app.Path, releaseDir)
|
|
// reset the release dir.
|
|
if err := os.RemoveAll(releasePath); err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
|
|
if err := os.MkdirAll(releasePath, 0o755); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
for _, t := range targets {
|
|
// build binary for a target, tarball it and save it under the release dir.
|
|
goos, goarch, err := gocmd.ParseTarget(t)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
out, err := os.MkdirTemp("", "")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer os.RemoveAll(out)
|
|
|
|
buildOptions := []exec.Option{
|
|
exec.StepOption(step.Env(
|
|
cmdrunner.Env(gocmd.EnvGOOS, goos),
|
|
cmdrunner.Env(gocmd.EnvGOARCH, goarch),
|
|
)),
|
|
}
|
|
|
|
if err := gocmd.BuildPath(ctx, out, binary, mainPath, buildFlags, buildOptions...); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
tarName := fmt.Sprintf("%s_%s_%s.tar.gz", prefix, goos, goarch)
|
|
tarPath := filepath.Join(releasePath, tarName)
|
|
|
|
tarf, err := os.Create(tarPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer tarf.Close()
|
|
|
|
if err := archive.CreateArchive(out, tarf); err != nil {
|
|
return "", errors.Errorf("error creating release archive: %w", err)
|
|
}
|
|
}
|
|
|
|
checksumPath := filepath.Join(releasePath, releaseChecksumKey)
|
|
|
|
// create a checksum.txt and return with the path to release dir.
|
|
return releasePath, checksum.Sum(releasePath, checksumPath)
|
|
}
|
|
|
|
func (c *Chain) preBuild(
|
|
ctx context.Context,
|
|
cacheStorage cache.Storage,
|
|
buildTags ...string,
|
|
) (buildFlags []string, err error) {
|
|
config, err := c.Config()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
chainID, err := c.ID()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ldFlags := config.Build.LDFlags
|
|
ldFlags = append(ldFlags,
|
|
fmt.Sprintf("-X github.com/cosmos/cosmos-sdk/version.Name=%s", xstrings.Title(c.app.Name)),
|
|
fmt.Sprintf("-X github.com/cosmos/cosmos-sdk/version.AppName=%sd", c.app.Name),
|
|
fmt.Sprintf("-X github.com/cosmos/cosmos-sdk/version.Version=%s", c.sourceVersion.tag),
|
|
fmt.Sprintf("-X github.com/cosmos/cosmos-sdk/version.Commit=%s", c.sourceVersion.hash),
|
|
fmt.Sprintf("-X github.com/cosmos/cosmos-sdk/version.BuildTags=%s", strings.Join(buildTags, ",")),
|
|
fmt.Sprintf("-X %s/cmd/%s/cmd.ChainID=%s", c.app.ImportPath, c.app.D(), chainID),
|
|
)
|
|
buildFlags = []string{
|
|
gocmd.FlagMod, gocmd.FlagModValueReadOnly,
|
|
gocmd.FlagTags, gocmd.Tags(buildTags...),
|
|
gocmd.FlagLdflags, gocmd.Ldflags(ldFlags...),
|
|
}
|
|
|
|
c.ev.Send("Installing dependencies...", events.ProgressUpdate())
|
|
|
|
// We do mod tidy before checking for checksum changes, because go.mod gets modified often
|
|
// and the mod verify command is the expensive one anyway
|
|
if err := gocmd.ModTidy(ctx, c.app.Path); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dirCache := cache.New[[]byte](cacheStorage, buildDirchangeCacheNamespace)
|
|
modChanged, err := dirchange.HasDirChecksumChanged(dirCache, modChecksumKey, c.app.Path, "go.mod")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if modChanged {
|
|
// By default no dependencies are checked to avoid issues with module
|
|
// ziphash files in case a Go workspace is being used.
|
|
if c.options.checkDependencies {
|
|
if err := gocmd.ModVerify(ctx, c.app.Path); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if err := dirchange.SaveDirChecksum(dirCache, modChecksumKey, c.app.Path, "go.mod"); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
c.ev.Send("Building the blockchain...", events.ProgressUpdate())
|
|
|
|
return buildFlags, nil
|
|
}
|
|
|
|
func (c *Chain) discoverMain(path string) (pkgPath string, err error) {
|
|
conf, err := c.Config()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if conf.Build.Main != "" {
|
|
return filepath.Join(c.app.Path, conf.Build.Main), nil
|
|
}
|
|
|
|
path, err = goanalysis.DiscoverOneMain(path)
|
|
if errors.Is(err, goanalysis.ErrMultipleMainPackagesFound) {
|
|
return "", errors.Wrap(err, "specify the path to your chain's main package in your config.yml>build.main")
|
|
}
|
|
return path, err
|
|
}
|