github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/osutil/buildid_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package osutil_test 21 22 import ( 23 "encoding/hex" 24 "io/ioutil" 25 "os" 26 "os/exec" 27 "path/filepath" 28 "regexp" 29 30 . "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/osutil" 33 ) 34 35 type buildIDSuite struct{} 36 37 var _ = Suite(&buildIDSuite{}) 38 39 var truePath = osutil.LookPathDefault("true", "/bin/true") 40 var falsePath = osutil.LookPathDefault("false", "/bin/false") 41 var gccPath = osutil.LookPathDefault("gcc", "/usr/bin/gcc") 42 43 func buildID(c *C, fname string) string { 44 // XXX host's 'file' command may be too old to know about Go BuildID or 45 // hexstring GNU BuildID, use with caution 46 output, err := exec.Command("file", fname).CombinedOutput() 47 c.Assert(err, IsNil) 48 49 c.Logf("file output: %q", string(output)) 50 51 // BuildID can look like: 52 // BuildID[sha1]=443877f9ec13c82365478130fc95cb5ff5181912 53 // BuildID[md5/uuid]=ae38cdf243d2111064dfee99dfc30013 54 // Go BuildID=YDAw4RLIEKpyxl90JbFQ/s9mld--03zAIIQ1tGb_5/aL-yPp ... 55 re := regexp.MustCompile(`BuildID(\[.*\]|)=([a-zA-Z0-9/_-]+)`) 56 matches := re.FindStringSubmatch(string(output)) 57 58 c.Assert(matches, HasLen, 3) 59 60 return matches[2] 61 } 62 63 func (s *buildIDSuite) TestReadBuildID(c *C) { 64 for _, fname := range []string{truePath, falsePath} { 65 66 id, err := osutil.ReadBuildID(fname) 67 c.Assert(err, IsNil) 68 c.Assert(id, Equals, buildID(c, fname), Commentf("executable: %s", fname)) 69 } 70 } 71 72 func (s *buildIDSuite) TestReadBuildIDNoID(c *C) { 73 stripedTruth := filepath.Join(c.MkDir(), "true") 74 osutil.CopyFile(truePath, stripedTruth, 0) 75 output, err := exec.Command("strip", "-R", ".note.gnu.build-id", stripedTruth).CombinedOutput() 76 c.Assert(string(output), Equals, "") 77 c.Assert(err, IsNil) 78 79 id, err := osutil.ReadBuildID(stripedTruth) 80 c.Assert(err, Equals, osutil.ErrNoBuildID) 81 c.Assert(id, Equals, "") 82 } 83 84 func (s *buildIDSuite) TestReadBuildIDmd5(c *C) { 85 if !osutil.FileExists(gccPath) { 86 c.Skip("No gcc found") 87 } 88 89 md5Truth := filepath.Join(c.MkDir(), "true") 90 err := ioutil.WriteFile(md5Truth+".c", []byte(`int main(){return 0;}`), 0644) 91 c.Assert(err, IsNil) 92 output, err := exec.Command(gccPath, "-Wl,--build-id=md5", "-xc", md5Truth+".c", "-o", md5Truth).CombinedOutput() 93 c.Assert(string(output), Equals, "") 94 c.Assert(err, IsNil) 95 96 id, err := osutil.ReadBuildID(md5Truth) 97 c.Assert(err, IsNil) 98 c.Assert(id, Equals, buildID(c, md5Truth)) 99 } 100 101 func (s *buildIDSuite) TestReadBuildIDFixedELF(c *C) { 102 if !osutil.FileExists(gccPath) { 103 c.Skip("No gcc found") 104 } 105 106 md5Truth := filepath.Join(c.MkDir(), "true") 107 err := ioutil.WriteFile(md5Truth+".c", []byte(`int main(){return 0;}`), 0644) 108 c.Assert(err, IsNil) 109 output, err := exec.Command(gccPath, "-Wl,--build-id=0xdeadcafe", "-xc", md5Truth+".c", "-o", md5Truth).CombinedOutput() 110 c.Assert(string(output), Equals, "") 111 c.Assert(err, IsNil) 112 113 id, err := osutil.ReadBuildID(md5Truth) 114 c.Assert(err, IsNil) 115 // XXX cannot call buildID() as the host's 'file' command may be too old 116 // to know about hexstring format of GNU BuildID 117 c.Assert(id, Equals, "deadcafe") 118 } 119 120 func (s *buildIDSuite) TestMyBuildID(c *C) { 121 restore := osutil.MockOsReadlink(func(string) (string, error) { 122 return truePath, nil 123 }) 124 defer restore() 125 126 id, err := osutil.MyBuildID() 127 c.Assert(err, IsNil) 128 c.Check(id, Equals, buildID(c, truePath)) 129 } 130 131 func (s *buildIDSuite) TestReadBuildGo(c *C) { 132 if os.Getenv("DH_GOPKG") != "" { 133 // Failure reason is unknown but only reproducible 134 // inside the any 21.04+ sbuild/pbuilder build 135 // environment during the build (with dh-golang). 136 // 137 // Not reproducible outside of dpkg-buildpackage. 138 c.Skip("This `go build` fails inside the dpkg-buildpackage environment with `loadinternal: cannot find runtime/cgo`") 139 } 140 141 tmpdir := c.MkDir() 142 goTruth := filepath.Join(tmpdir, "true") 143 err := ioutil.WriteFile(goTruth+".go", []byte(`package main; func main(){}`), 0644) 144 c.Assert(err, IsNil) 145 // force specific Go BuildID 146 cmd := exec.Command("go", "build", "-o", goTruth, "-ldflags=-buildid=foobar", goTruth+".go") 147 // set custom homedir to ensure tests work in an sbuild environment 148 // that force a non-existing homedir 149 cmd.Env = append(os.Environ(), "HOME="+tmpdir) 150 output, err := cmd.CombinedOutput() 151 c.Assert(string(output), Equals, "") 152 c.Assert(err, IsNil) 153 154 id, err := osutil.ReadBuildID(goTruth) 155 c.Assert(err, IsNil) 156 157 // ReadBuildID returns a hex encoded string, however buildID() 158 // returns the "raw" string so we need to decode first 159 decoded, err := hex.DecodeString(id) 160 c.Assert(err, IsNil) 161 // XXX cannot call buildID() as the host's 'file' command may be too old 162 // to know about Go BuildID 163 c.Assert(string(decoded), Equals, "foobar") 164 }