github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/tools/winresource/main.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 // This is a utility which binds to libkb to get the correct version 5 // for printing out or generating compiled resources for the windows 6 // executable. 7 8 //go:build windows 9 // +build windows 10 11 package main 12 13 import ( 14 "flag" 15 "fmt" 16 "io" 17 "log" 18 "os" 19 "os/exec" 20 "reflect" 21 "time" 22 23 "strconv" 24 25 "github.com/akavel/rsrc/binutil" 26 "github.com/akavel/rsrc/coff" 27 "github.com/josephspurrier/goversioninfo" 28 "github.com/keybase/client/go/libkb" 29 ) 30 31 func getBuildName() string { 32 // Todo: use regular build number when not doing prerelease 33 34 gitHash, err := exec.Command("cmd", "/C", "git", "log", "-1", "--pretty=format:%h").Output() 35 if err != nil { 36 log.Print("Error generating githash", err) 37 os.Exit(3) 38 } 39 40 return fmt.Sprintf("%d%02d%02d%02d%02d%02d+%s", time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), gitHash) 41 42 } 43 44 // kbWriteSyso creates a resource file from the version info and optionally an icon. 45 // arch must be an architecture string accepted by coff.Arch, like "386" or "amd64" 46 // Note this is partially lifted from josephspurrier/goversioninfo, which we 47 // extend here to write multiple icons. 48 func kbWriteSyso(vi *goversioninfo.VersionInfo, filename string, arch string, icons []string) error { 49 50 // Channel for generating IDs 51 newID := make(chan uint16) 52 go func() { 53 for i := uint16(1); ; i++ { 54 newID <- i 55 } 56 }() 57 58 // Create a new RSRC section 59 coff := coff.NewRSRC() 60 61 // Set the architechture 62 err := coff.Arch(arch) 63 if err != nil { 64 return err 65 } 66 67 // ID 16 is for Version Information 68 coff.AddResource(16, 1, goversioninfo.SizedReader{Buffer: &vi.Buffer}) 69 70 // NOTE: if/when we start using a manifest, it goes here 71 72 // If icon is enabled 73 if vi.IconPath != "" { 74 if err := addIcon(coff, vi.IconPath, newID); err != nil { 75 return err 76 } 77 } 78 // if extra icons were passed in 79 for _, i := range icons { 80 if i != "" { 81 if err := addIcon(coff, i, newID); err != nil { 82 return err 83 } 84 } 85 } 86 87 coff.Freeze() 88 89 // Write to file 90 return writeCoff(coff, filename) 91 } 92 93 // From josephspurrier/goversioninfo 94 func writeCoff(coff *coff.Coff, fnameout string) error { 95 out, err := os.Create(fnameout) 96 if err != nil { 97 return err 98 } 99 if err = writeCoffTo(out, coff); err != nil { 100 return fmt.Errorf("error writing %q: %v", fnameout, err) 101 } 102 return nil 103 } 104 105 // From josephspurrier/goversioninfo 106 func writeCoffTo(w io.WriteCloser, coff *coff.Coff) error { 107 bw := binutil.Writer{W: w} 108 109 // write the resulting file to disk 110 binutil.Walk(coff, func(v reflect.Value, path string) error { 111 if binutil.Plain(v.Kind()) { 112 bw.WriteLE(v.Interface()) 113 return nil 114 } 115 vv, ok := v.Interface().(binutil.SizedReader) 116 if ok { 117 bw.WriteFromSized(vv) 118 return binutil.WALK_SKIP 119 } 120 return nil 121 }) 122 123 err := bw.Err 124 if closeErr := w.Close(); closeErr != nil && err == nil { 125 err = closeErr 126 } 127 return err 128 } 129 130 // Create the syso and custom build file 131 func main() { 132 133 outPtr := flag.String("o", "rsrc_windows.syso", "resource output pathname") 134 printverPtr := flag.Bool("v", false, "print version to console (no .syso output)") 135 printCustomVerPtr := flag.Bool("cv", false, "print custom version to console (no .syso output)") 136 printCustomBuildPtr := flag.Bool("cb", false, "print custom build number to console (no .syso output)") 137 printWinVerPtr := flag.Bool("w", false, "print windows format version to console (no .syso output)") 138 iconPtr := flag.String("i", "../../media/icons/Keybase.ico", "icon pathname") 139 kbfsIconPtr := flag.String("kbfsicon", "", "icon pathname") 140 fileDescriptionPtr := flag.String("d", "Keybase utility", "File Description") 141 originalFilenamePtr := flag.String("n", "keybase.exe", "File name") 142 143 flag.Parse() 144 145 var fv goversioninfo.FileVersion 146 147 if int, err := fmt.Sscanf(libkb.Version, "%d.%d.%d", &fv.Major, &fv.Minor, &fv.Patch); int != 3 || err != nil { 148 log.Printf("Error parsing version %v", err) 149 os.Exit(3) 150 } 151 152 fv.Build, _ = strconv.Atoi(os.Getenv("KEYBASE_WINBUILD")) 153 if fv.Build == 0 && libkb.PrereleaseBuild != "" { 154 fv.Build, _ = strconv.Atoi(libkb.PrereleaseBuild) 155 } 156 157 semVer := fmt.Sprintf("%d.%d.%d-%d", fv.Major, fv.Minor, fv.Patch, fv.Build) 158 159 if *printverPtr { 160 fmt.Print(semVer) 161 return 162 } 163 164 if *printWinVerPtr { 165 fmt.Printf("%d.%d.%d.%d", fv.Major, fv.Minor, fv.Patch, fv.Build) 166 return 167 } 168 169 if *printCustomVerPtr { 170 customVer := fmt.Sprintf("%d.%d.%d-%s", fv.Major, fv.Minor, fv.Patch, getBuildName()) 171 fmt.Print(customVer) 172 return 173 } 174 175 if *printCustomBuildPtr { 176 fmt.Printf("%s", getBuildName()) 177 return 178 } 179 180 // Create a new container 181 vi := &goversioninfo.VersionInfo{ 182 FixedFileInfo: goversioninfo.FixedFileInfo{ 183 FileVersion: fv, 184 ProductVersion: fv, 185 FileFlagsMask: "3f", 186 FileFlags: "00", 187 FileOS: "040004", 188 FileType: "01", 189 FileSubType: "00", 190 }, 191 192 StringFileInfo: goversioninfo.StringFileInfo{ 193 CompanyName: "Keybase, Inc.", 194 FileDescription: *fileDescriptionPtr, 195 InternalName: "Keybase", 196 LegalCopyright: "Copyright (c) 2015, Keybase", 197 OriginalFilename: *originalFilenamePtr, 198 ProductName: "Keybase", 199 ProductVersion: libkb.VersionString(), 200 }, 201 VarFileInfo: goversioninfo.VarFileInfo{ 202 Translation: goversioninfo.Translation{ 203 LangID: 0x409, // english 204 CharsetID: 0x4B0, // unicode 205 }, 206 }, 207 } 208 209 // Fill the structures with config data 210 vi.Build() 211 212 // Write the data to a buffer 213 vi.Walk() 214 215 // Optionally, embed an icon by path 216 // If the icon has multiple sizes, all of the sizes will be embedded 217 vi.IconPath = *iconPtr 218 219 // Create the file 220 if err := kbWriteSyso(vi, *outPtr, "386", []string{*kbfsIconPtr}); err != nil { 221 log.Printf("Error writing %s: %v", *outPtr, err) 222 os.Exit(3) 223 } 224 225 }