diff --git a/Makefile b/Makefile index 525e4bf..0e3e9ba 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,13 @@ .PHONY: all prod fmt test testfmt clean +# Make doesn't have a built-in and reliable way to get the number of jobs... +JOBS ?= 1 + all: - @v run build.vsh + @v run build.vsh --cpus=$(JOBS) prod: - @v run build.vsh -prod + @v run build.vsh -prod --cpus=$(JOBS) fmt: v fmt -w . diff --git a/README.md b/README.md index 9082f41..0095162 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ the command input/output consistent across the tools. ## Building Running `make` or `v run build.vsh` will build all the programs in `bin/`. +Use `JOBS=N make` or `v run build.vsh --cpus=N` to compile in parallel. Note: support for access to user account info (via utmp) is limited to POSIX-like platforms. And, so, for Windows, utilities requiring utmp support (uptime, users, who, whoami) are currently diff --git a/build.vsh b/build.vsh index 3174194..3112ad4 100755 --- a/build.vsh +++ b/build.vsh @@ -1,6 +1,13 @@ #!/bin/env v import time +import runtime +import flag +import os + +struct Config { + jobs int @[long: cpus; short: 'j'] +} const ignore_dirs = { 'windows': [ @@ -41,12 +48,10 @@ const ignore_dirs = { unbuffer_stdout() -dump(user_os()) -dump(ignore_dirs) - -args := arguments() -vargs := if args.len > 1 { args[1..] } else { [] } -dump(vargs) +config, remaining := flag.to_struct[Config](os.args, skip: 1)! +mut jobs := if config.jobs > 0 { config.jobs } else { runtime.nr_cpus() } +mut vargs := remaining.clone() +println('Using ${jobs} parallel jobs') curdir := getwd() chdir('src')! @@ -60,6 +65,8 @@ if !exists('${curdir}/bin') { sw_total := time.new_stopwatch() mut compiled := 0 mut already_compiled := 0 +mut dirs_to_compile := []string{} + for dir in dirs { if dir in ignore_dirs { continue @@ -85,16 +92,48 @@ for dir in dirs { } } - mut final_args := '-Wimpure-v' - for arg in vargs { - final_args += ' ' + arg + dirs_to_compile << dir +} + +// Now compile in parallel +ch := chan bool{cap: jobs} +results_ch := chan bool{cap: dirs_to_compile.len} +print_ch := chan string{cap: 100} + +// Start printer thread, avoiding garbled text when building +spawn fn [print_ch] () { + for { + msg := <-print_ch or { break } + print(msg) } - print('compiling ${dir:-20s}...') - cmd := @VEXE + ' ${final_args} -o ${curdir}/bin/${dir} ./${dir}' - sw := time.new_stopwatch() - execute_or_panic(cmd) - println(' took ${sw.elapsed().milliseconds()}ms .') - compiled++ +}() + +for dir in dirs_to_compile { + // Acquire the current slot + ch <- true + spawn fn [ch, results_ch, dir, curdir, vargs, print_ch] () { + defer { + results_ch <- true + // Released + _ := <-ch + } + mut final_args := '-Wimpure-v' + for arg in vargs { + final_args += ' ' + arg + } + cmd := @VEXE + ' ${final_args} -o "${curdir}/bin/${dir}" "./${dir}"' + sw := time.new_stopwatch() + execute_or_panic(cmd) + print_ch <- 'compiling ${dir:-20s}... took ${sw.elapsed().milliseconds()}ms .\n' + }() } + +// Wait for all compilations to finish +for _ in 0 .. dirs_to_compile.len { + _ := <-results_ch +} + +print_ch.close() +compiled = dirs_to_compile.len println('> Compiled: ${compiled:3} tools in ${sw_total.elapsed().milliseconds()}ms. Already compiled and skipped: ${already_compiled} . All folders: ${dirs.len} .') chdir(curdir)!