X Tutup
Skip to content

Commit 39431a1

Browse files
committed
Port select portions of Makefile to script/build.go
This is to enable build tasks on Windows.
1 parent 5e97775 commit 39431a1

File tree

4 files changed

+215
-32
lines changed

4 files changed

+215
-32
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
/site
77
.github/**/node_modules
88
/CHANGELOG.md
9+
/script/build
910

1011
# VS Code
1112
.vscode

Makefile

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,49 @@
1-
BUILD_FILES = $(shell go list -f '{{range .GoFiles}}{{$$.Dir}}/{{.}}\
2-
{{end}}' ./...)
3-
4-
GH_VERSION ?= $(shell git describe --tags 2>/dev/null || git rev-parse --short HEAD)
5-
DATE_FMT = +%Y-%m-%d
6-
ifdef SOURCE_DATE_EPOCH
7-
BUILD_DATE ?= $(shell date -u -d "@$(SOURCE_DATE_EPOCH)" "$(DATE_FMT)" 2>/dev/null || date -u -r "$(SOURCE_DATE_EPOCH)" "$(DATE_FMT)" 2>/dev/null || date -u "$(DATE_FMT)")
8-
else
9-
BUILD_DATE ?= $(shell date "$(DATE_FMT)")
10-
endif
11-
121
CGO_CPPFLAGS ?= ${CPPFLAGS}
132
export CGO_CPPFLAGS
143
CGO_CFLAGS ?= ${CFLAGS}
154
export CGO_CFLAGS
165
CGO_LDFLAGS ?= $(filter -g -L% -l% -O%,${LDFLAGS})
176
export CGO_LDFLAGS
187

19-
GO_LDFLAGS := -X github.com/cli/cli/internal/build.Version=$(GH_VERSION) $(GO_LDFLAGS)
20-
GO_LDFLAGS := -X github.com/cli/cli/internal/build.Date=$(BUILD_DATE) $(GO_LDFLAGS)
21-
ifdef GH_OAUTH_CLIENT_SECRET
22-
GO_LDFLAGS := -X github.com/cli/cli/internal/authflow.oauthClientID=$(GH_OAUTH_CLIENT_ID) $(GO_LDFLAGS)
23-
GO_LDFLAGS := -X github.com/cli/cli/internal/authflow.oauthClientSecret=$(GH_OAUTH_CLIENT_SECRET) $(GO_LDFLAGS)
24-
endif
8+
.PHONY: bin/gh
9+
bin/gh: script/build
10+
@script/build bin/gh
2511

26-
bin/gh: $(BUILD_FILES)
27-
go build -trimpath -ldflags "${GO_LDFLAGS}" -o "$@" ./cmd/gh
12+
script/build: script/build.go
13+
go build -o script/build script/build.go
2814

29-
clean:
30-
rm -rf ./bin ./share
3115
.PHONY: clean
16+
clean: script/build
17+
@script/build clean
3218

19+
.PHONY: manpages
20+
manpages: script/build
21+
@script/build manpages
22+
23+
.PHONY: test
3324
test:
3425
go test ./...
35-
.PHONY: test
3626

3727
site:
3828
git clone https://github.com/github/cli.github.com.git "$@"
3929

30+
.PHONY: site-docs
4031
site-docs: site
4132
git -C site pull
4233
git -C site rm 'manual/gh*.md' 2>/dev/null || true
4334
go run ./cmd/gen-docs --website --doc-path site/manual
4435
rm -f site/manual/*.bak
4536
git -C site add 'manual/gh*.md'
4637
git -C site commit -m 'update help docs' || true
47-
.PHONY: site-docs
4838

39+
.PHONY: site-bump
4940
site-bump: site-docs
5041
ifndef GITHUB_REF
5142
$(error GITHUB_REF is not set)
5243
endif
5344
sed -i.bak -E 's/(assign version = )".+"/\1"$(GITHUB_REF:refs/tags/v%=%)"/' site/index.html
5445
rm -f site/index.html.bak
5546
git -C site commit -m '$(GITHUB_REF:refs/tags/v%=%)' index.html
56-
.PHONY: site-bump
57-
58-
.PHONY: manpages
59-
manpages:
60-
go run ./cmd/gen-docs --man-page --doc-path ./share/man/man1/
6147

6248
DESTDIR :=
6349
prefix := /usr/local

docs/source.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,18 @@
2222
# installs to '/usr/local' by default; sudo may be required
2323
$ make install
2424

25-
# install to a different location
25+
# or, install to a different location
2626
$ make install prefix=/path/to/gh
2727
```
2828

2929
#### Windows
3030
```sh
31-
# build the binary
32-
> go build -o gh.exe ./cmd/gh
31+
# build the `bin\gh.exe` binary
32+
> go run script/build.go
3333
```
3434
There is no install step available on Windows.
3535

3636
3. Run `gh version` to check if it worked.
3737

3838
#### Windows
39-
Run `.\gh version` to check if it worked.
39+
Run `bin\gh version` to check if it worked.

script/build.go

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// Build tasks for the GitHub CLI project.
2+
//
3+
// Usage: go run script/build.go [<task>]
4+
//
5+
// Known tasks are:
6+
//
7+
// bin/gh:
8+
// Builds the main executable.
9+
// Supported environment variables:
10+
// - GH_VERSION: determined from source by default
11+
// - GH_OAUTH_CLIENT_ID
12+
// - GH_OAUTH_CLIENT_SECRET
13+
// - SOURCE_DATE_EPOCH: enables reproducible builds
14+
// - GO_LDFLAGS
15+
//
16+
// manpages:
17+
// Builds the man pages under `share/man/man1/`.
18+
//
19+
// clean:
20+
// Deletes all built files.
21+
//
22+
23+
package main
24+
25+
import (
26+
"fmt"
27+
"io/ioutil"
28+
"os"
29+
"os/exec"
30+
"path/filepath"
31+
"runtime"
32+
"strconv"
33+
"strings"
34+
"time"
35+
36+
"github.com/cli/safeexec"
37+
)
38+
39+
var tasks = map[string]func(string) error{
40+
"bin/gh": func(exe string) error {
41+
info, err := os.Stat(exe)
42+
if err == nil && !sourceFilesLaterThan(info.ModTime()) {
43+
fmt.Printf("%s: `%s` is up to date.\n", self, exe)
44+
return nil
45+
}
46+
47+
ldflags := os.Getenv("GO_LDFLAGS")
48+
ldflags = fmt.Sprintf("-X github.com/cli/cli/internal/build.Version=%s %s", version(), ldflags)
49+
ldflags = fmt.Sprintf("-X github.com/cli/cli/internal/build.Date=%s %s", date(), ldflags)
50+
if oauthSecret := os.Getenv("GH_OAUTH_CLIENT_SECRET"); oauthSecret != "" {
51+
ldflags = fmt.Sprintf("-X github.com/cli/cli/internal/authflow.oauthClientSecret=%s %s", oauthSecret, ldflags)
52+
ldflags = fmt.Sprintf("-X github.com/cli/cli/internal/authflow.oauthClientID=%s %s", os.Getenv("GH_OAUTH_CLIENT_ID"), ldflags)
53+
}
54+
55+
return run("go", "build", "-trimpath", "-ldflags", ldflags, "-o", exe, "./cmd/gh")
56+
},
57+
"manpages": func(_ string) error {
58+
return run("go", "run", "./cmd/gen-docs", "--man-page", "--doc-path", "./share/man/man1/")
59+
},
60+
"clean": func(_ string) error {
61+
return rmrf("bin", "share")
62+
},
63+
}
64+
65+
var self string
66+
67+
func main() {
68+
task := "bin/gh"
69+
if runtime.GOOS == "windows" {
70+
task = "bin\\gh.exe"
71+
}
72+
73+
if len(os.Args) > 1 {
74+
task = os.Args[1]
75+
}
76+
77+
self = filepath.Base(os.Args[0])
78+
if self == "build" {
79+
self = "build.go"
80+
}
81+
82+
t := tasks[normalizeTask(task)]
83+
if t == nil {
84+
fmt.Fprintf(os.Stderr, "Don't know how to build task `%s`.\n", task)
85+
os.Exit(1)
86+
}
87+
88+
err := t(task)
89+
if err != nil {
90+
fmt.Fprintln(os.Stderr, err)
91+
fmt.Fprintf(os.Stderr, "%s: building task `%s` failed.\n", self, task)
92+
os.Exit(1)
93+
}
94+
}
95+
96+
func version() string {
97+
if versionEnv := os.Getenv("GH_VERSION"); versionEnv != "" {
98+
return versionEnv
99+
}
100+
if desc, err := cmdOutput("git", "describe", "--tags"); err == nil {
101+
return desc
102+
}
103+
rev, _ := cmdOutput("git", "rev-parse", "--short", "HEAD")
104+
return rev
105+
}
106+
107+
func date() string {
108+
t := time.Now()
109+
if sourceDate := os.Getenv("SOURCE_DATE_EPOCH"); sourceDate != "" {
110+
if sec, err := strconv.ParseInt(sourceDate, 10, 64); err == nil {
111+
t = time.Unix(sec, 0)
112+
}
113+
}
114+
return t.Format("2006-01-02")
115+
}
116+
117+
func sourceFilesLaterThan(t time.Time) bool {
118+
foundLater := false
119+
_ = filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
120+
if err != nil {
121+
return err
122+
}
123+
if foundLater {
124+
return filepath.SkipDir
125+
}
126+
if len(path) > 1 && (path[0] == '.' || path[0] == '_') {
127+
if info.IsDir() {
128+
return filepath.SkipDir
129+
} else {
130+
return nil
131+
}
132+
}
133+
if info.IsDir() {
134+
return nil
135+
}
136+
if path == "go.mod" || path == "go.sum" || (strings.HasSuffix(path, ".go") && !strings.HasSuffix(path, "_test.go")) {
137+
if info.ModTime().After(t) {
138+
foundLater = true
139+
}
140+
}
141+
return nil
142+
})
143+
return foundLater
144+
}
145+
146+
func rmrf(targets ...string) error {
147+
args := append([]string{"rm", "-rf"}, targets...)
148+
announce(args...)
149+
for _, target := range targets {
150+
if err := os.RemoveAll(target); err != nil {
151+
return err
152+
}
153+
}
154+
return nil
155+
}
156+
157+
func announce(args ...string) {
158+
fmt.Println(shellInspect(args))
159+
}
160+
161+
func run(args ...string) error {
162+
exe, err := safeexec.LookPath(args[0])
163+
if err != nil {
164+
return err
165+
}
166+
announce(args...)
167+
cmd := exec.Command(exe, args[1:]...)
168+
return cmd.Run()
169+
}
170+
171+
func cmdOutput(args ...string) (string, error) {
172+
exe, err := safeexec.LookPath(args[0])
173+
if err != nil {
174+
return "", err
175+
}
176+
cmd := exec.Command(exe, args[1:]...)
177+
cmd.Stderr = ioutil.Discard
178+
out, err := cmd.Output()
179+
return strings.TrimSuffix(string(out), "\n"), err
180+
}
181+
182+
func shellInspect(args []string) string {
183+
fmtArgs := make([]string, len(args))
184+
for i, arg := range args {
185+
if strings.ContainsAny(arg, " \t'\"") {
186+
fmtArgs[i] = fmt.Sprintf("%q", arg)
187+
} else {
188+
fmtArgs[i] = arg
189+
}
190+
}
191+
return strings.Join(fmtArgs, " ")
192+
}
193+
194+
func normalizeTask(t string) string {
195+
return filepath.ToSlash(strings.TrimSuffix(t, ".exe"))
196+
}

0 commit comments

Comments
 (0)
X Tutup