v/vlib/v/builder/rebuilding.v

240 lines
6.9 KiB
V

module builder
import os
import hash
import strings
import v.util
import v.pref
import v.vcache
pub fn (mut b Builder) rebuild_modules() {
if !b.pref.use_cache || b.pref.build_mode == .build_module {
return
}
util.timing_start('${@METHOD} source_hashing')
mut new_hashes := map[string]string{}
mut old_hashes := map[string]string{}
mut sb_new_hashes := strings.new_builder(1024)
all_files := b.parsed_files.map(it.path)
//
mut cm := vcache.new_cache_manager(all_files)
sold_hashes := cm.load('.hashes', 'all_files') or { ' ' }
// eprintln(sold_hashes)
sold_hashes_lines := sold_hashes.split('\n')
for line in sold_hashes_lines {
if line.len == 0 {
continue
}
x := line.split(' ')
chash := x[0]
cpath := x[1]
old_hashes[cpath] = chash
}
// eprintln('old_hashes: $old_hashes')
for p in b.parsed_files {
cpath := p.path
ccontent := util.read_file(cpath) or { '' }
chash := hash.sum64_string(ccontent, 7).hex_full()
new_hashes[cpath] = chash
sb_new_hashes.write_string(chash)
sb_new_hashes.write_byte(` `)
sb_new_hashes.write_string(cpath)
sb_new_hashes.write_byte(`\n`)
}
snew_hashes := sb_new_hashes.str()
// eprintln('new_hashes: $new_hashes')
// eprintln('> new_hashes != old_hashes: ' + ( old_hashes != new_hashes ).str())
// eprintln(snew_hashes)
cm.save('.hashes', 'all_files', snew_hashes) or {}
util.timing_measure('${@METHOD} source_hashing')
if new_hashes != old_hashes {
util.timing_start('${@METHOD} rebuilding')
// eprintln('> b.mod_invalidates_paths: $b.mod_invalidates_paths')
// eprintln('> b.mod_invalidates_mods: $b.mod_invalidates_mods')
// eprintln('> b.path_invalidates_mods: $b.path_invalidates_mods')
$if trace_invalidations ? {
for k, v in b.mod_invalidates_paths {
mut m := map[string]bool{}
for mm in b.mod_invalidates_mods[k] {
m[mm] = true
}
eprintln('> module `$k` invalidates: $m.keys()')
for fpath in v {
eprintln(' $fpath')
}
}
}
mut invalidated_paths := map[string]int{}
mut invalidated_mod_paths := map[string]int{}
for npath, nhash in new_hashes {
if npath !in old_hashes {
invalidated_paths[npath]++
continue
}
if old_hashes[npath] != nhash {
invalidated_paths[npath]++
continue
}
}
for opath, ohash in old_hashes {
if opath !in new_hashes {
invalidated_paths[opath]++
continue
}
if new_hashes[opath] != ohash {
invalidated_paths[opath]++
continue
}
}
$if trace_invalidations ? {
eprintln('invalidated_paths: $invalidated_paths')
}
mut rebuild_everything := false
for cycle := 0; true; cycle++ {
$if trace_invalidations ? {
eprintln('> cycle: $cycle | invalidated_paths: $invalidated_paths')
}
mut new_invalidated_paths := map[string]int{}
for npath, _ in invalidated_paths {
invalidated_mods := b.path_invalidates_mods[npath]
if invalidated_mods == ['main'] {
continue
}
if 'builtin' in invalidated_mods {
// When `builtin` is invalid, there is no point in
// extracting a finer grained dependency resolution
// of the dependencies any more. Instead, just rebuild
// every module.
rebuild_everything = true
break
}
for imod in invalidated_mods {
if imod == 'main' {
continue
}
for np in b.mod_invalidates_paths[imod] {
new_invalidated_paths[np]++
}
}
$if trace_invalidations ? {
eprintln('> npath -> invalidated_mods | $npath -> $invalidated_mods')
}
mpath := os.dir(npath)
invalidated_mod_paths[mpath]++
}
if rebuild_everything {
break
}
if new_invalidated_paths.len == 0 {
break
}
invalidated_paths = new_invalidated_paths.clone()
}
if rebuild_everything {
invalidated_mod_paths = {}
for npath, _ in new_hashes {
mpath := os.dir(npath)
pimods := b.path_invalidates_mods[npath]
if pimods == ['main'] {
continue
}
invalidated_mod_paths[mpath]++
}
}
$if trace_invalidations ? {
eprintln('invalidated_mod_paths: $invalidated_mod_paths')
eprintln('rebuild_everything: $rebuild_everything')
}
if invalidated_mod_paths.len > 0 {
impaths := invalidated_mod_paths.keys()
vexe := pref.vexe_path()
for imp in impaths {
b.v_build_module(vexe, imp)
}
}
util.timing_measure('${@METHOD} rebuilding')
}
}
fn (mut b Builder) v_build_module(vexe string, imp_path string) {
pwd := os.getwd()
defer {
os.chdir(pwd) or {}
}
// do run `v build-module x` always in main vfolder; x can be a relative path
vroot := os.dir(vexe)
os.chdir(vroot) or {}
boptions := b.pref.build_options.join(' ')
rebuild_cmd := '${os.quoted_path(vexe)} $boptions build-module ${os.quoted_path(imp_path)}'
vcache.dlog('| Builder.' + @FN, 'vexe: $vexe | imp_path: $imp_path | rebuild_cmd: $rebuild_cmd')
$if trace_v_build_module ? {
eprintln('> Builder.v_build_module: $rebuild_cmd')
}
os.system(rebuild_cmd)
}
fn (mut b Builder) rebuild_cached_module(vexe string, imp_path string) string {
res := b.pref.cache_manager.exists('.o', imp_path) or {
if b.pref.is_verbose {
println('Cached $imp_path .o file not found... Building .o file for $imp_path')
}
b.v_build_module(vexe, imp_path)
rebuilded_o := b.pref.cache_manager.exists('.o', imp_path) or {
panic('could not rebuild cache module for $imp_path, error: $err.msg()')
}
return rebuilded_o
}
return res
}
fn (mut b Builder) handle_usecache(vexe string) {
if !b.pref.use_cache || b.pref.build_mode == .build_module {
return
}
mut libs := []string{} // builtin.o os.o http.o etc
mut built_modules := []string{}
builtin_obj_path := b.rebuild_cached_module(vexe, 'vlib/builtin')
libs << builtin_obj_path
for ast_file in b.parsed_files {
if b.pref.is_test && ast_file.mod.name != 'main' {
imp_path := b.find_module_path(ast_file.mod.name, ast_file.path) or {
verror('cannot import module "$ast_file.mod.name" (not found)')
break
}
obj_path := b.rebuild_cached_module(vexe, imp_path)
libs << obj_path
built_modules << ast_file.mod.name
}
for imp_stmt in ast_file.imports {
imp := imp_stmt.mod
// strconv is already imported inside builtin, so skip generating its object file
// TODO: incase we have other modules with the same name, make sure they are vlib
// is this even doign anything?
if imp in ['strconv', 'strings', 'dlmalloc'] {
continue
}
if imp in built_modules {
continue
}
if util.should_bundle_module(imp) {
continue
}
// The problem is cmd/v is in module main and imports
// the relative module named help, which is built as cmd.v.help not help
// currently this got this workign by building into main, see ast.FnDecl in cgen
if imp == 'help' {
continue
}
imp_path := b.find_module_path(imp, ast_file.path) or {
verror('cannot import module "$imp" (not found)')
break
}
obj_path := b.rebuild_cached_module(vexe, imp_path)
libs << obj_path
built_modules << imp
}
}
b.ccoptions.post_args << libs
}