github.com/ethereum/go-ethereum@v1.14.3/internal/version/version.go (about)

     1  // Copyright 2022 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package version implements reading of build version information.
    18  package version
    19  
    20  import (
    21  	"fmt"
    22  	"runtime"
    23  	"runtime/debug"
    24  	"strings"
    25  
    26  	"github.com/ethereum/go-ethereum/params"
    27  )
    28  
    29  const ourPath = "github.com/ethereum/go-ethereum" // Path to our module
    30  
    31  // These variables are set at build-time by the linker when the build is
    32  // done by build/ci.go.
    33  var gitCommit, gitDate string
    34  
    35  // VCSInfo represents the git repository state.
    36  type VCSInfo struct {
    37  	Commit string // head commit hash
    38  	Date   string // commit time in YYYYMMDD format
    39  	Dirty  bool
    40  }
    41  
    42  // VCS returns version control information of the current executable.
    43  func VCS() (VCSInfo, bool) {
    44  	if gitCommit != "" {
    45  		// Use information set by the build script if present.
    46  		return VCSInfo{Commit: gitCommit, Date: gitDate}, true
    47  	}
    48  	if buildInfo, ok := debug.ReadBuildInfo(); ok {
    49  		if buildInfo.Main.Path == ourPath {
    50  			return buildInfoVCS(buildInfo)
    51  		}
    52  	}
    53  	return VCSInfo{}, false
    54  }
    55  
    56  // ClientName creates a software name/version identifier according to common
    57  // conventions in the Ethereum p2p network.
    58  func ClientName(clientIdentifier string) string {
    59  	git, _ := VCS()
    60  	return fmt.Sprintf("%s/v%v/%v-%v/%v",
    61  		strings.Title(clientIdentifier),
    62  		params.VersionWithCommit(git.Commit, git.Date),
    63  		runtime.GOOS, runtime.GOARCH,
    64  		runtime.Version(),
    65  	)
    66  }
    67  
    68  // Info returns build and platform information about the current binary.
    69  //
    70  // If the package that is currently executing is a prefixed by our go-ethereum
    71  // module path, it will print out commit and date VCS information. Otherwise,
    72  // it will assume it's imported by a third-party and will return the imported
    73  // version and whether it was replaced by another module.
    74  func Info() (version, vcs string) {
    75  	version = params.VersionWithMeta
    76  	buildInfo, ok := debug.ReadBuildInfo()
    77  	if !ok {
    78  		return version, ""
    79  	}
    80  	version = versionInfo(buildInfo)
    81  	if status, ok := VCS(); ok {
    82  		modified := ""
    83  		if status.Dirty {
    84  			modified = " (dirty)"
    85  		}
    86  		commit := status.Commit
    87  		if len(commit) > 8 {
    88  			commit = commit[:8]
    89  		}
    90  		vcs = commit + "-" + status.Date + modified
    91  	}
    92  	return version, vcs
    93  }
    94  
    95  // versionInfo returns version information for the currently executing
    96  // implementation.
    97  //
    98  // Depending on how the code is instantiated, it returns different amounts of
    99  // information. If it is unable to determine which module is related to our
   100  // package it falls back to the hardcoded values in the params package.
   101  func versionInfo(info *debug.BuildInfo) string {
   102  	// If the main package is from our repo, prefix version with "geth".
   103  	if strings.HasPrefix(info.Path, ourPath) {
   104  		return fmt.Sprintf("geth %s", info.Main.Version)
   105  	}
   106  	// Not our main package, so explicitly print out the module path and
   107  	// version.
   108  	var version string
   109  	if info.Main.Path != "" && info.Main.Version != "" {
   110  		// These can be empty when invoked with "go run".
   111  		version = fmt.Sprintf("%s@%s ", info.Main.Path, info.Main.Version)
   112  	}
   113  	mod := findModule(info, ourPath)
   114  	if mod == nil {
   115  		// If our module path wasn't imported, it's unclear which
   116  		// version of our code they are running. Fallback to hardcoded
   117  		// version.
   118  		return version + fmt.Sprintf("geth %s", params.VersionWithMeta)
   119  	}
   120  	// Our package is a dependency for the main module. Return path and
   121  	// version data for both.
   122  	version += fmt.Sprintf("%s@%s", mod.Path, mod.Version)
   123  	if mod.Replace != nil {
   124  		// If our package was replaced by something else, also note that.
   125  		version += fmt.Sprintf(" (replaced by %s@%s)", mod.Replace.Path, mod.Replace.Version)
   126  	}
   127  	return version
   128  }
   129  
   130  // findModule returns the module at path.
   131  func findModule(info *debug.BuildInfo, path string) *debug.Module {
   132  	if info.Path == ourPath {
   133  		return &info.Main
   134  	}
   135  	for _, mod := range info.Deps {
   136  		if mod.Path == path {
   137  			return mod
   138  		}
   139  	}
   140  	return nil
   141  }