172 lines
4.5 KiB
V
172 lines
4.5 KiB
V
module vmod
|
|
|
|
import os
|
|
|
|
// This file provides a caching mechanism for seeking quickly whether a
|
|
// given folder has a v.mod file in it or in any of its parent folders.
|
|
//
|
|
// ModFileCacher.get(folder) works in such a way, that given this tree:
|
|
// examples/hanoi.v
|
|
// vlib/v.mod
|
|
// vlib/v/tests/project_with_c_code/mod1/v.mod
|
|
// vlib/v/tests/project_with_c_code/mod1/wrapper.v
|
|
// -----------------
|
|
// ModFileCacher.get('examples')
|
|
// => ModFileAndFolder{'', 'examples'}
|
|
// ModFileCacher.get('vlib/v/tests')
|
|
// => ModFileAndFolder{'vlib/v.mod', 'vlib'}
|
|
// ModFileCacher.get('vlib/v')
|
|
// => ModFileAndFolder{'vlib/v.mod', 'vlib'}
|
|
// ModFileCacher.get('vlib/v/test/project_with_c_code/mod1')
|
|
// => ModFileAndFolder{'vlib/v/test/project_with_c_code/mod1/v.mod', 'vlib/v/test/project_with_c_code/mod1'}
|
|
pub struct ModFileAndFolder {
|
|
pub:
|
|
// vmod_file contains the full path of the found 'v.mod' file, or ''
|
|
// if no 'v.mod' file was found in file_path_dir, or in its parent folders.
|
|
vmod_file string
|
|
// vmod_folder contains the file_path_dir, if there is no 'v.mod' file in
|
|
// *any* of the parent folders, otherwise it is the first parent folder,
|
|
// where a v.mod file was found.
|
|
vmod_folder string
|
|
}
|
|
|
|
[heap]
|
|
pub struct ModFileCacher {
|
|
mut:
|
|
cache map[string]ModFileAndFolder
|
|
// folder_files caches os.ls(key)
|
|
folder_files map[string][]string
|
|
}
|
|
|
|
pub fn new_mod_file_cacher() &ModFileCacher {
|
|
return &ModFileCacher{}
|
|
}
|
|
|
|
pub fn (mcache &ModFileCacher) debug() {
|
|
$if debug {
|
|
eprintln('ModFileCacher DUMP:')
|
|
eprintln(' ModFileCacher.cache:')
|
|
for k, v in mcache.cache {
|
|
eprintln(' K: ${k:-32s} | V: "${v.vmod_file:32s}" | "${v.vmod_folder:32s}" ')
|
|
}
|
|
eprintln(' ModFileCacher.folder_files:')
|
|
for k, v in mcache.folder_files {
|
|
eprintln(' K: ${k:-32s} | V: $v.str()')
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn (mut mcache ModFileCacher) get_by_file(vfile string) ModFileAndFolder {
|
|
return mcache.get_by_folder(os.dir(vfile))
|
|
}
|
|
|
|
pub fn (mut mcache ModFileCacher) get_by_folder(vfolder string) ModFileAndFolder {
|
|
mfolder := os.real_path(vfolder)
|
|
if mfolder in mcache.cache {
|
|
return mcache.cache[mfolder]
|
|
}
|
|
traversed_folders, res := mcache.traverse(mfolder)
|
|
for tfolder in traversed_folders {
|
|
mcache.add(tfolder, res)
|
|
}
|
|
return res
|
|
}
|
|
|
|
fn (mut cacher ModFileCacher) add(path string, result ModFileAndFolder) {
|
|
cacher.cache[path] = result
|
|
}
|
|
|
|
fn (mut mcache ModFileCacher) traverse(mfolder string) ([]string, ModFileAndFolder) {
|
|
mut cfolder := mfolder
|
|
mut folders_so_far := [cfolder]
|
|
mut levels := 0
|
|
for {
|
|
if levels > 255 {
|
|
break
|
|
}
|
|
if cfolder == '/' || cfolder == '' {
|
|
break
|
|
}
|
|
if cfolder in mcache.cache {
|
|
res := mcache.cache[cfolder]
|
|
if res.vmod_file.len == 0 {
|
|
mcache.mark_folders_as_vmod_free(folders_so_far)
|
|
} else {
|
|
mcache.mark_folders_with_vmod(folders_so_far, res)
|
|
}
|
|
return []string{}, res
|
|
}
|
|
files := mcache.get_files(cfolder)
|
|
if 'v.mod' in files {
|
|
// TODO: actually read the v.mod file and parse its contents to see
|
|
// if its source folder is different
|
|
res := ModFileAndFolder{
|
|
vmod_file: os.join_path(cfolder, 'v.mod')
|
|
vmod_folder: cfolder
|
|
}
|
|
return folders_so_far, res
|
|
}
|
|
if mcache.check_for_stop(cfolder, files) {
|
|
break
|
|
}
|
|
cfolder = os.dir(cfolder)
|
|
folders_so_far << cfolder
|
|
levels++
|
|
}
|
|
mcache.mark_folders_as_vmod_free(folders_so_far)
|
|
return [mfolder], ModFileAndFolder{
|
|
vmod_file: ''
|
|
vmod_folder: mfolder
|
|
}
|
|
}
|
|
|
|
fn (mut mcache ModFileCacher) mark_folders_with_vmod(folders_so_far []string, vmod ModFileAndFolder) {
|
|
for f in folders_so_far {
|
|
mcache.add(f, vmod)
|
|
}
|
|
}
|
|
|
|
fn (mut mcache ModFileCacher) mark_folders_as_vmod_free(folders_so_far []string) {
|
|
// No need to check these folders anymore,
|
|
// because their parents do not contain v.mod files
|
|
for f in folders_so_far {
|
|
mcache.add(f, vmod_file: '', vmod_folder: f)
|
|
}
|
|
}
|
|
|
|
const (
|
|
mod_file_stop_paths = ['.git', '.hg', '.svn', '.v.mod.stop']
|
|
)
|
|
|
|
fn (mcache &ModFileCacher) check_for_stop(cfolder string, files []string) bool {
|
|
for i in vmod.mod_file_stop_paths {
|
|
if i in files {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn (mut mcache ModFileCacher) get_files(cfolder string) []string {
|
|
if cfolder in mcache.folder_files {
|
|
return mcache.folder_files[cfolder]
|
|
}
|
|
mut files := []string{}
|
|
if os.exists(cfolder) && os.is_dir(cfolder) {
|
|
if listing := os.ls(cfolder) {
|
|
files = listing.clone()
|
|
}
|
|
}
|
|
mcache.folder_files[cfolder] = files
|
|
return files
|
|
}
|
|
|
|
// used during lookup for v.mod to support @VROOT
|
|
const (
|
|
private_file_cacher = new_mod_file_cacher()
|
|
)
|
|
|
|
pub fn get_cache() &ModFileCacher {
|
|
return vmod.private_file_cacher
|
|
}
|