v2 / vlib / v / vmod / vmod.v
195 lines · 177 sloc · 5.55 KB · 5518bfc7d5534e061bef8fdb3fef4e0280bae558
Raw
1module vmod
2
3import os
4
5const mod_file_stop_paths = ['.git', '.hg', '.svn', '.v.mod.stop']
6
7// used during lookup for v.mod to support @VEXEROOT
8const private_file_cacher = new_mod_file_cacher()
9
10pub fn get_cache() &ModFileCacher {
11 return private_file_cacher
12}
13
14// resolved_base_url returns the source folder configured by `base_url`,
15// resolved relative to the folder containing the `v.mod` file.
16pub fn (manifest Manifest) resolved_base_url(vmod_root string) string {
17 if manifest.base_url == '' {
18 return ''
19 }
20 return os.norm_path(os.join_path(vmod_root, manifest.base_url))
21}
22
23// source_root returns the folder where sources are looked up under a `v.mod`.
24// When `base_url` is set, it points at that folder; otherwise it falls back to
25// the folder containing `v.mod`. The previous implicit `src/` fallback is gone.
26pub fn (manifest Manifest) source_root(vmod_root string) string {
27 base_url := manifest.resolved_base_url(vmod_root)
28 if base_url != '' {
29 return base_url
30 }
31 return os.norm_path(vmod_root)
32}
33
34// This file provides a caching mechanism for seeking quickly whether a
35// given folder has a v.mod file in it or in any of its parent folders.
36//
37// ModFileCacher.get(folder) works in such a way, that given this tree:
38// examples/hanoi.v
39// vlib/v.mod
40// vlib/v/tests/project_with_c_code/mod1/v.mod
41// vlib/v/tests/project_with_c_code/mod1/wrapper.c.v
42// -----------------
43// ModFileCacher.get('examples')
44// => ModFileAndFolder{'', 'examples'}
45// ModFileCacher.get('vlib/v/tests')
46// => ModFileAndFolder{'vlib/v.mod', 'vlib'}
47// ModFileCacher.get('vlib/v')
48// => ModFileAndFolder{'vlib/v.mod', 'vlib'}
49// ModFileCacher.get('vlib/v/test/project_with_c_code/mod1')
50// => ModFileAndFolder{'vlib/v/test/project_with_c_code/mod1/v.mod', 'vlib/v/test/project_with_c_code/mod1'}
51pub struct ModFileAndFolder {
52pub:
53 // vmod_file contains the full path of the found 'v.mod' file, or ''
54 // if no 'v.mod' file was found in file_path_dir, or in its parent folders.
55 vmod_file string
56 // vmod_folder contains the file_path_dir, if there is no 'v.mod' file in
57 // *any* of the parent folders, otherwise it is the first parent folder,
58 // where a v.mod file was found.
59 vmod_folder string
60}
61
62@[heap]
63pub struct ModFileCacher {
64mut:
65 cache map[string]ModFileAndFolder
66 // folder_files caches os.ls(key)
67 folder_files map[string][]string
68 hits int
69 misses int
70 get_files_hits int
71 get_files_misses int
72}
73
74pub fn new_mod_file_cacher() &ModFileCacher {
75 return &ModFileCacher{}
76}
77
78@[if debug_mod_file_cacher ?]
79pub fn (mcache &ModFileCacher) debug() {
80 eprintln('ModFileCacher hits: ${mcache.hits}, misses: ${mcache.misses} | get_files_hits: ${mcache.get_files_hits} | get_files_misses: ${mcache.get_files_misses}')
81 eprintln(' ModFileCacher.cache.len: ${mcache.cache.len}')
82 for k, v in mcache.cache {
83 eprintln(' K: ${k:-42s} | v.mod: ${v.vmod_file:-42s} | folder: `${v.vmod_folder}`')
84 }
85 eprintln(' ModFileCacher.folder_files:')
86 for k, v in mcache.folder_files {
87 eprintln(' K: ${k:-42s} | folder_files: ${v}')
88 }
89}
90
91pub fn (mut mcache ModFileCacher) get_by_file(vfile string) ModFileAndFolder {
92 return mcache.get_by_folder(os.dir(vfile))
93}
94
95pub fn (mut mcache ModFileCacher) get_by_folder(vfolder string) ModFileAndFolder {
96 mfolder := os.real_path(vfolder)
97 if mfolder in mcache.cache {
98 mcache.hits++
99 return mcache.cache[mfolder]
100 }
101 traversed_folders, res := mcache.traverse(mfolder)
102 for tfolder in traversed_folders {
103 mcache.add(tfolder, res)
104 }
105 mcache.misses++
106 return res
107}
108
109fn (mut cacher ModFileCacher) add(path string, result ModFileAndFolder) {
110 cacher.cache[path] = result
111}
112
113fn (mut mcache ModFileCacher) traverse(mfolder string) ([]string, ModFileAndFolder) {
114 mut cfolder := mfolder
115 mut folders_so_far := [cfolder]
116 mut levels := 0
117 for {
118 if levels > 255 {
119 break
120 }
121 if cfolder == '/' || cfolder == '' {
122 break
123 }
124 if cfolder in mcache.cache {
125 mcache.hits++
126 res := mcache.cache[cfolder]
127 if res.vmod_file.len == 0 {
128 mcache.mark_folders_as_vmod_free(folders_so_far)
129 } else {
130 mcache.mark_folders_with_vmod(folders_so_far, res)
131 }
132 return []string{}, res
133 }
134 files := mcache.get_files(cfolder)
135 if 'v.mod' in files {
136 // TODO: actually read the v.mod file and parse its contents to see
137 // if its source folder is different
138 res := ModFileAndFolder{
139 vmod_file: os.join_path(cfolder, 'v.mod')
140 vmod_folder: cfolder
141 }
142 return folders_so_far, res
143 }
144 if mcache.check_for_stop(files) {
145 break
146 }
147 cfolder = os.dir(cfolder)
148 folders_so_far << cfolder
149 levels++
150 }
151 mcache.mark_folders_as_vmod_free(folders_so_far)
152 return [mfolder], ModFileAndFolder{
153 vmod_file: ''
154 vmod_folder: mfolder
155 }
156}
157
158fn (mut mcache ModFileCacher) mark_folders_with_vmod(folders_so_far []string, vmod ModFileAndFolder) {
159 for f in folders_so_far {
160 mcache.add(f, vmod)
161 }
162}
163
164fn (mut mcache ModFileCacher) mark_folders_as_vmod_free(folders_so_far []string) {
165 // No need to check these folders anymore,
166 // because their parents do not contain v.mod files
167 for f in folders_so_far {
168 mcache.add(f, vmod_file: '', vmod_folder: f)
169 }
170}
171
172fn (mcache &ModFileCacher) check_for_stop(files []string) bool {
173 for i in mod_file_stop_paths {
174 if i in files {
175 return true
176 }
177 }
178 return false
179}
180
181fn (mut mcache ModFileCacher) get_files(cfolder string) []string {
182 if cfolder in mcache.folder_files {
183 mcache.get_files_hits++
184 return mcache.folder_files[cfolder]
185 }
186 mcache.get_files_misses++
187 mut files := []string{}
188 if os.exists(cfolder) && os.is_dir(cfolder) {
189 if listing := os.ls(cfolder) {
190 files = listing.clone()
191 }
192 }
193 mcache.folder_files[cfolder] = files
194 return files
195}
196