Skip to content

Commit 7369a0e

Browse files
aykevldeadprogram
authored andcommitted
all: add support for Windows
1 parent 8906192 commit 7369a0e

File tree

11 files changed

+157
-28
lines changed

11 files changed

+157
-28
lines changed

Makefile

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11

22
# aliases
33
all: tinygo
4-
tinygo: build/tinygo
54

65
# Default build and source directories, as created by `make llvm-build`.
76
LLVM_BUILDDIR ?= llvm-build
@@ -33,28 +32,55 @@ else
3332
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
3433
endif
3534

36-
.PHONY: all tinygo build/tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-avr
35+
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-avr
3736

3837
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines debuginfodwarf executionengine instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target
3938

40-
UNAME_S := $(shell uname -s)
41-
ifeq ($(UNAME_S),Linux)
39+
ifeq ($(OS),Windows_NT)
40+
EXE = .exe
4241
START_GROUP = -Wl,--start-group
4342
END_GROUP = -Wl,--end-group
44-
else ifeq ($(UNAME_S),Darwin)
43+
44+
# LLVM compiled using MinGW on Windows appears to have problems with threads.
45+
# Without this flag, linking results in errors like these:
46+
# libLLVMSupport.a(Threading.cpp.obj):Threading.cpp:(.text+0x55): undefined reference to `std::thread::hardware_concurrency()'
47+
LLVM_OPTION += -DLLVM_ENABLE_THREADS=OFF
48+
49+
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
50+
CGO_LDFLAGS_EXTRA += -lversion
51+
52+
# Build libclang manually because the CMake-based build system on Windows
53+
# doesn't allow building libclang as a static library.
54+
LIBCLANG_PATH = $(abspath build/libclang-custom.a)
55+
LIBCLANG_FILES = $(abspath $(wildcard $(LLVM_BUILDDIR)/tools/clang/tools/libclang/CMakeFiles/libclang.dir/*.cpp.obj))
56+
57+
# Add the libclang dependency to the tinygo binary target.
58+
tinygo: $(LIBCLANG_PATH)
59+
test: $(LIBCLANG_PATH)
60+
# Build libclang.
61+
$(LIBCLANG_PATH): $(LIBCLANG_FILES)
62+
@mkdir -p build
63+
ar rcs $(LIBCLANG_PATH) $^
64+
65+
else ifeq ($(shell uname -s),Darwin)
4566
MD5SUM = md5
67+
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
68+
else
69+
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
70+
START_GROUP = -Wl,--start-group
71+
END_GROUP = -Wl,--end-group
4672
endif
4773

48-
CLANG_LIBS = $(START_GROUP) $(abspath $(LLVM_BUILDDIR))/lib/libclang.a -lclangAnalysis -lclangARCMigrate -lclangAST -lclangASTMatchers -lclangBasic -lclangCodeGen -lclangCrossTU -lclangDriver -lclangDynamicASTMatchers -lclangEdit -lclangFormat -lclangFrontend -lclangFrontendTool -lclangHandleCXX -lclangHandleLLVM -lclangIndex -lclangLex -lclangParse -lclangRewrite -lclangRewriteFrontend -lclangSema -lclangSerialization -lclangStaticAnalyzerCheckers -lclangStaticAnalyzerCore -lclangStaticAnalyzerFrontend -lclangTooling -lclangToolingASTDiff -lclangToolingCore -lclangToolingInclusions $(END_GROUP) -lstdc++
74+
CLANG_LIBS = $(START_GROUP) -lclangAnalysis -lclangARCMigrate -lclangAST -lclangASTMatchers -lclangBasic -lclangCodeGen -lclangCrossTU -lclangDriver -lclangDynamicASTMatchers -lclangEdit -lclangFormat -lclangFrontend -lclangFrontendTool -lclangHandleCXX -lclangHandleLLVM -lclangIndex -lclangLex -lclangParse -lclangRewrite -lclangRewriteFrontend -lclangSema -lclangSerialization -lclangStaticAnalyzerCheckers -lclangStaticAnalyzerCore -lclangStaticAnalyzerFrontend -lclangTooling -lclangToolingASTDiff -lclangToolingCore -lclangToolingInclusions $(END_GROUP) -lstdc++
4975

5076
LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -llldMachO -llldMinGW -llldReaderWriter -llldWasm -llldYAML $(END_GROUP)
5177

5278

5379
# For static linking.
54-
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config)","")
80+
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
5581
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
5682
CGO_CXXFLAGS=-std=c++11
57-
CGO_LDFLAGS=-L$(LLVM_BUILDDIR)/lib $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS))
83+
CGO_LDFLAGS+=$(LIBCLANG_PATH) -std=c++11 -L$(abspath $(LLVM_BUILDDIR)/lib) $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
5884
endif
5985

6086

@@ -108,9 +134,9 @@ $(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
108134

109135

110136
# Build the Go compiler.
111-
build/tinygo:
137+
tinygo:
112138
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
113-
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -o build/tinygo -tags byollvm .
139+
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -o build/tinygo$(EXE) -tags byollvm .
114140

115141
test:
116142
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -tags byollvm ./interp ./transform .
@@ -198,7 +224,7 @@ endif
198224
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/export
199225
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/main
200226

201-
release: build/tinygo gen-device
227+
release: tinygo gen-device
202228
@mkdir -p build/release/tinygo/bin
203229
@mkdir -p build/release/tinygo/lib/clang/include
204230
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
@@ -208,7 +234,7 @@ release: build/tinygo gen-device
208234
@mkdir -p build/release/tinygo/pkg/armv7m-none-eabi
209235
@mkdir -p build/release/tinygo/pkg/armv7em-none-eabi
210236
@echo copying source files
211-
@cp -p build/tinygo build/release/tinygo/bin
237+
@cp -p build/tinygo$(EXE) build/release/tinygo/bin
212238
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
213239
@cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
214240
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS

azure-pipelines.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
jobs:
2+
- job: Build
3+
timeoutInMinutes: 180
4+
pool:
5+
vmImage: 'VS2017-Win2016'
6+
steps:
7+
- checkout: self
8+
- task: CacheBeta@0
9+
displayName: Cache LLVM source
10+
inputs:
11+
key: llvm-source-8-windows-v0
12+
path: llvm-project
13+
- task: Bash@3
14+
displayName: Download LLVM source
15+
inputs:
16+
targetType: inline
17+
script: make llvm-source
18+
- task: CacheBeta@0
19+
displayName: Cache LLVM build
20+
inputs:
21+
key: llvm-build-8-windows-v1
22+
path: llvm-build
23+
- task: Bash@3
24+
displayName: Build LLVM
25+
inputs:
26+
targetType: inline
27+
script: |
28+
if [ ! -f llvm-build/lib/liblldELF.a ]
29+
then
30+
choco install ninja
31+
make llvm-build
32+
fi
33+
- task: Bash@3
34+
displayName: Install QEMU
35+
inputs:
36+
targetType: inline
37+
script: choco install qemu
38+
- task: Bash@3
39+
displayName: Test TinyGo
40+
inputs:
41+
targetType: inline
42+
script: |
43+
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
44+
make test
45+
- task: Bash@3
46+
displayName: Build TinyGo release tarball
47+
inputs:
48+
targetType: inline
49+
script: |
50+
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
51+
make release -j4
52+
- publish: $(System.DefaultWorkingDirectory)/build/release.tar.gz
53+
displayName: Publish tarball as artifact
54+
artifact: tinygo.windows-amd64.tar.gz
55+
- task: Bash@3
56+
displayName: Smoke tests
57+
inputs:
58+
targetType: inline
59+
script: |
60+
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
61+
make smoketest TINYGO=build/tinygo AVR=0 RISCV=0

buildcache.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,10 @@ func moveFile(src, dst string) error {
109109
return err
110110
}
111111

112-
err = os.Rename(dst+".tmp", dst)
112+
err = outf.Close()
113113
if err != nil {
114114
return err
115115
}
116116

117-
return outf.Close()
117+
return os.Rename(dst+".tmp", dst)
118118
}

commands.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ func init() {
2424
commands["ld.lld"] = append(commands["ld.lld"], "/usr/local/opt/llvm/bin/ld.lld")
2525
commands["wasm-ld"] = append(commands["wasm-ld"], "/usr/local/opt/llvm/bin/wasm-ld")
2626
}
27+
// Add the path for when LLVM was installed with the installer from
28+
// llvm.org, which by default doesn't add LLVM to the $PATH environment
29+
// variable.
30+
if runtime.GOOS == "windows" {
31+
commands["clang"] = append(commands["clang"], "clang", "C:\\Program Files\\LLVM\\bin\\clang.exe")
32+
commands["ld.lld"] = append(commands["ld.lld"], "lld", "C:\\Program Files\\LLVM\\bin\\lld.exe")
33+
commands["wasm-ld"] = append(commands["wasm-ld"], "C:\\Program Files\\LLVM\\bin\\wasm-ld.exe")
34+
}
2735
}
2836

2937
func execCommand(cmdNames []string, args ...string) error {
@@ -33,7 +41,7 @@ func execCommand(cmdNames []string, args ...string) error {
3341
cmd.Stderr = os.Stderr
3442
err := cmd.Run()
3543
if err != nil {
36-
if err, ok := err.(*exec.Error); ok && err.Err == exec.ErrNotFound {
44+
if err, ok := err.(*exec.Error); ok && (err.Err == exec.ErrNotFound || err.Err.Error() == "file does not exist") {
3745
// this command was not found, try the next
3846
continue
3947
}

interp/interp_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ func fuzzyEqualIR(s1, s2 string) bool {
8787
func filterIrrelevantIRLines(lines []string) []string {
8888
var out []string
8989
for _, line := range lines {
90+
line = strings.TrimSpace(line) // drop '\r' on Windows
9091
if line == "" || line[0] == ';' {
9192
continue
9293
}

loader/loader.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,9 +317,9 @@ func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
317317
defer rd.Close()
318318
relpath := path
319319
if filepath.IsAbs(path) {
320-
relpath, err = filepath.Rel(p.Dir, path)
321-
if err != nil {
322-
return nil, err
320+
rp, err := filepath.Rel(p.Dir, path)
321+
if err == nil {
322+
relpath = rp
323323
}
324324
}
325325
return parser.ParseFile(p.fset, relpath, rd, mode)

main.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -552,11 +552,7 @@ func FlashGDB(pkgName, target, port string, ocdOutput bool, config *BuildConfig)
552552
}
553553
// Make sure the daemon doesn't receive Ctrl-C that is intended for
554554
// GDB (to break the currently executing program).
555-
// https://stackoverflow.com/a/35435038/559350
556-
daemon.SysProcAttr = &syscall.SysProcAttr{
557-
Setpgid: true,
558-
Pgid: 0,
559-
}
555+
setCommandAsDaemon(daemon)
560556
// Start now, and kill it on exit.
561557
daemon.Start()
562558
defer func() {

main_test.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@ func TestCompiler(t *testing.T) {
4545
}
4646
defer os.RemoveAll(tmpdir)
4747

48-
t.Log("running tests on host...")
49-
for _, path := range matches {
50-
t.Run(path, func(t *testing.T) {
51-
runTest(path, tmpdir, "", t)
52-
})
48+
if runtime.GOOS != "windows" {
49+
t.Log("running tests on host...")
50+
for _, path := range matches {
51+
t.Run(path, func(t *testing.T) {
52+
runTest(path, tmpdir, "", t)
53+
})
54+
}
5355
}
5456

5557
if testing.Short() {
@@ -162,6 +164,7 @@ func runTest(path, tmpdir string, target string, t *testing.T) {
162164

163165
// putchar() prints CRLF, convert it to LF.
164166
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
167+
expected = bytes.Replace(expected, []byte{'\r', '\n'}, []byte{'\n'}, -1) // for Windows
165168

166169
// Check whether the command ran successfully.
167170
fail := false

transform/transform_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ func fuzzyEqualIR(s1, s2 string) bool {
7070
func filterIrrelevantIRLines(lines []string) []string {
7171
var out []string
7272
for _, line := range lines {
73+
line = strings.TrimSpace(line) // drop '\r' on Windows
7374
if line == "" || line[0] == ';' {
7475
continue
7576
}

util_unix.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// +build !windows
2+
3+
package main
4+
5+
// This file contains utility functions for Unix-like systems (e.g. Linux).
6+
7+
import (
8+
"os/exec"
9+
"syscall"
10+
)
11+
12+
// setCommandAsDaemon makes sure this command does not receive signals sent to
13+
// the parent.
14+
func setCommandAsDaemon(daemon *exec.Cmd) {
15+
// https://stackoverflow.com/a/35435038/559350
16+
daemon.SysProcAttr = &syscall.SysProcAttr{
17+
Setpgid: true,
18+
Pgid: 0,
19+
}
20+
}

0 commit comments

Comments
 (0)