Skip to content

Commit 9bbb233

Browse files
committed
main: include prebuilt compiler-rt libraries in release tarball
This avoids depending on clang-7 to build compiler-rt for the most common ARM microcontrollers.
1 parent 5b50759 commit 9bbb233

File tree

4 files changed

+109
-32
lines changed

4 files changed

+109
-32
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ release: static gen-device
117117
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
118118
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
119119
@mkdir -p build/release/tinygo/lib/nrfx
120+
@mkdir -p build/release/tinygo/pkg/armv6m-none-eabi
121+
@mkdir -p build/release/tinygo/pkg/armv7m-none-eabi
122+
@mkdir -p build/release/tinygo/pkg/armv7em-none-eabi
120123
@cp -p build/tinygo build/release/tinygo/bin
121124
@cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
122125
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
@@ -126,6 +129,9 @@ release: static gen-device
126129
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
127130
@cp -rp src build/release/tinygo/src
128131
@cp -rp targets build/release/tinygo/targets
132+
./build/tinygo build-builtins -target=armv6m-none-eabi -o build/release/tinygo/pkg/armv6m-none-eabi/compiler-rt.a
133+
./build/tinygo build-builtins -target=armv7m-none-eabi -o build/release/tinygo/pkg/armv7m-none-eabi/compiler-rt.a
134+
./build/tinygo build-builtins -target=armv7em-none-eabi -o build/release/tinygo/pkg/armv7em-none-eabi/compiler-rt.a
129135
tar -czf build/release.tar.gz -C build/release tinygo
130136

131137
# Binary that can run on the host.

buildcache.go

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -80,29 +80,45 @@ func cacheStore(tmppath, name, configKey string, sourceFiles []string) (string,
8080
return "", err
8181
}
8282
cachepath := filepath.Join(dir, name)
83-
err = os.Rename(tmppath, cachepath)
83+
err = moveFile(tmppath, cachepath)
8484
if err != nil {
85-
inf, err := os.Open(tmppath)
86-
if err != nil {
87-
return "", err
88-
}
89-
defer inf.Close()
90-
outf, err := os.Create(cachepath + ".tmp")
91-
if err != nil {
92-
return "", err
93-
}
85+
return "", err
86+
}
87+
return cachepath, nil
88+
}
9489

95-
_, err = io.Copy(outf, inf)
96-
if err != nil {
97-
return "", err
98-
}
90+
// moveFile renames the file from src to dst. If renaming doesn't work (for
91+
// example, the rename crosses a filesystem boundary), the file is copied and
92+
// the old file is removed.
93+
func moveFile(src, dst string) error {
94+
err := os.Rename(src, dst)
95+
if err == nil {
96+
// Success!
97+
return nil
98+
}
99+
// Failed to move, probably a different filesystem.
100+
// Do a copy + remove.
101+
inf, err := os.Open(src)
102+
if err != nil {
103+
return err
104+
}
105+
defer inf.Close()
106+
outpath := dst + ".tmp"
107+
outf, err := os.Create(outpath)
108+
if err != nil {
109+
return err
110+
}
99111

100-
err = os.Rename(cachepath+".tmp", cachepath)
101-
if err != nil {
102-
return "", err
103-
}
112+
_, err = io.Copy(outf, inf)
113+
if err != nil {
114+
os.Remove(outpath)
115+
return err
116+
}
104117

105-
return cachepath, outf.Close()
118+
err = os.Rename(dst+".tmp", dst)
119+
if err != nil {
120+
return err
106121
}
107-
return cachepath, nil
122+
123+
return outf.Close()
108124
}

builtins.go

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,29 @@ var aeabiBuiltins = []string{
157157

158158
func builtinFiles(target string) []string {
159159
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
160-
if target[:3] == "arm" {
160+
if strings.HasPrefix(target, "arm") {
161161
builtins = append(builtins, aeabiBuiltins...)
162162
}
163163
return builtins
164164
}
165165

166+
// builtinsDir returns the directory where the sources for compiler-rt are kept.
167+
func builtinsDir() string {
168+
return filepath.Join(sourceDir(), "lib", "compiler-rt", "lib", "builtins")
169+
}
170+
166171
// Get the builtins archive, possibly generating it as needed.
167172
func loadBuiltins(target string) (path string, err error) {
173+
// Try to load a precompiled compiler-rt library.
174+
precompiledPath := filepath.Join(sourceDir(), "pkg", target, "compiler-rt.a")
175+
if _, err := os.Stat(precompiledPath); err == nil {
176+
// Found a precompiled compiler-rt for this OS/architecture. Return the
177+
// path directly.
178+
return precompiledPath, nil
179+
}
180+
168181
outfile := "librt-" + target + ".a"
169-
builtinsDir := filepath.Join(sourceDir(), "lib", "compiler-rt", "lib", "builtins")
182+
builtinsDir := builtinsDir()
170183

171184
builtins := builtinFiles(target)
172185
srcs := make([]string, len(builtins))
@@ -178,9 +191,33 @@ func loadBuiltins(target string) (path string, err error) {
178191
return path, err
179192
}
180193

181-
dir, err := ioutil.TempDir("", "tinygo-builtins")
194+
var cachepath string
195+
err = compileBuiltins(target, func(path string) error {
196+
path, err := cacheStore(path, outfile, commands["clang"], srcs)
197+
cachepath = path
198+
return err
199+
})
200+
return cachepath, err
201+
}
202+
203+
// compileBuiltins compiles builtins from compiler-rt into a static library.
204+
// When it succeeds, it will call the callback with the resulting path. The path
205+
// will be removed after callback returns. If callback returns an error, this is
206+
// passed through to the return value of this function.
207+
func compileBuiltins(target string, callback func(path string) error) error {
208+
builtinsDir := builtinsDir()
209+
210+
builtins := builtinFiles(target)
211+
srcs := make([]string, len(builtins))
212+
for i, name := range builtins {
213+
srcs[i] = filepath.Join(builtinsDir, name)
214+
}
215+
216+
dirPrefix := "tinygo-builtins"
217+
remapDir := filepath.Join(os.TempDir(), dirPrefix)
218+
dir, err := ioutil.TempDir(os.TempDir(), dirPrefix)
182219
if err != nil {
183-
return "", err
220+
return err
184221
}
185222
defer os.RemoveAll(dir)
186223

@@ -195,13 +232,16 @@ func loadBuiltins(target string) (path string, err error) {
195232
objpath := filepath.Join(dir, objname+".o")
196233
objs = append(objs, objpath)
197234
srcpath := filepath.Join(builtinsDir, name)
198-
cmd := exec.Command(commands["clang"], "-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "--target="+target, "-o", objpath, srcpath)
235+
// Note: -fdebug-prefix-map is necessary to make the output archive
236+
// reproducible. Otherwise the temporary directory is stored in the
237+
// archive itself, which varies each run.
238+
cmd := exec.Command(commands["clang"], "-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir, "-o", objpath, srcpath)
199239
cmd.Stdout = os.Stdout
200240
cmd.Stderr = os.Stderr
201241
cmd.Dir = dir
202242
err = cmd.Run()
203243
if err != nil {
204-
return "", &commandError{"failed to build", srcpath, err}
244+
return &commandError{"failed to build", srcpath, err}
205245
}
206246
}
207247

@@ -213,8 +253,10 @@ func loadBuiltins(target string) (path string, err error) {
213253
cmd.Dir = dir
214254
err = cmd.Run()
215255
if err != nil {
216-
return "", &commandError{"failed to make static library", arpath, err}
256+
return &commandError{"failed to make static library", arpath, err}
217257
}
218258

219-
return cacheStore(arpath, outfile, commands["clang"], srcs)
259+
// Give the caller the resulting file. The callback must copy the file,
260+
// because after it returns the temporary directory will be removed.
261+
return callback(arpath)
220262
}

main.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,21 +186,20 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
186186

187187
// Load builtins library from the cache, possibly compiling it on the
188188
// fly.
189-
var cachePath string
189+
var librt string
190190
if spec.RTLib == "compiler-rt" {
191-
librt, err := loadBuiltins(spec.Triple)
191+
librt, err = loadBuiltins(spec.Triple)
192192
if err != nil {
193193
return err
194194
}
195-
cachePath, _ = filepath.Split(librt)
196195
}
197196

198197
// Prepare link command.
199198
executable := filepath.Join(dir, "main")
200199
tmppath := executable // final file
201200
ldflags := append(spec.LDFlags, "-o", executable, objfile)
202201
if spec.RTLib == "compiler-rt" {
203-
ldflags = append(ldflags, "-L", cachePath, "-lrt-"+spec.Triple)
202+
ldflags = append(ldflags, librt)
204203
}
205204

206205
// Compile extra files.
@@ -554,6 +553,20 @@ func main() {
554553
}
555554
err := Build(flag.Arg(0), *outpath, target, config)
556555
handleCompilerError(err)
556+
case "build-builtins":
557+
// Note: this command is only meant to be used while making a release!
558+
if *outpath == "" {
559+
fmt.Fprintln(os.Stderr, "No output filename supplied (-o).")
560+
usage()
561+
os.Exit(1)
562+
}
563+
if *target == "" {
564+
fmt.Fprintln(os.Stderr, "No target (-target).")
565+
}
566+
err := compileBuiltins(*target, func(path string) error {
567+
return moveFile(path, *outpath)
568+
})
569+
handleCompilerError(err)
557570
case "flash", "gdb":
558571
if *outpath != "" {
559572
fmt.Fprintln(os.Stderr, "Output cannot be specified with the flash command.")

0 commit comments

Comments
 (0)