/
vmod.v
167 lines (151 loc) · 4.48 KB
/
vmod.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
module vmod
import os
const mod_file_stop_paths = ['.git', '.hg', '.svn', '.v.mod.stop']
// 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
}
// 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.c.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)
}
}
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
}